Comment sérialiser un objet dans une liste de paramètres de requête URL?


149

Sans connaître les clés d'un JavaScript Object, comment puis-je transformer quelque chose comme ...

var obj = {
   param1: 'something',
   param2: 'somethingelse',
   param3: 'another'
}

obj[param4] = 'yetanother';

...dans...

var str = 'param1=something&param2=somethingelse&param3=another&param4=yetanother';

...?


Vous recherchez une solution récursive?
Jared Farrish le

1
@Jared j'ai ajouté une solution récursive :)
alex

@alex - Merci; J'aime voir les réponses des gens les plus expérimentés sur les problèmes les plus compliqués. :)
Jared Farrish

2
@Jared Vous savez, je ne me considère jamais vraiment comme un développeur JavaScript expérimenté . Plus comme hack 'til it works guy :)
alex

@alex - Oh oui, moi aussi. Mais comment ce que vous avez mis en place serait-il comparable à la façon dont je l'aurais abordé? Je suis constamment étonné.
Jared Farrish le

Réponses:


104
var str = "";
for (var key in obj) {
    if (str != "") {
        str += "&";
    }
    str += key + "=" + encodeURIComponent(obj[key]);
}

Exemple: http://jsfiddle.net/WFPen/


3
Pourquoi ne pas utiliser une fonction avec récursivité?
Jared Farrish le

1
merci @aroth! J'ai seulement accepté la réponse de @ patrick ci-dessus plutôt que la vôtre (elles sont essentiellement les mêmes) parce qu'il était le premier, je crois. Je suis très reconnaissant de votre réponse.
bobsoap le

4
@bobsoap: Je pense que @aroth était un peu en avance sur moi, alors je coche @aroth toutes choses étant égales par ailleurs.
user113716

3
L'obj [key] ne devrait-il pas être enveloppé dans encodeURIComponent ()? Que se passe-t-il si «quelque chose d'autre» était «autre chose»?
James S

1
Je suis presque sûr que cela ne devrait pas être la réponse acceptée, ou presque toutes les réponses sur ce thread stackoverflow. La raison principale étant qu'aucune d'entre elles, à l'exception de la réponse possible de @ zac ci-dessous, satisfera correctement l'encodage de cet { a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" };objet.Il semble que @UnLoCo ait suggéré une bibliothèque NPM qui fonctionnera également, ce qui extrait la méthode param fonctionnelle de jQuery pour qu'elle soit autonome.
Wes


120

Un élégant: (en supposant que vous exécutiez un navigateur ou un nœud moderne)

var str = Object.keys(obj).map(function(key) {
  return key + '=' + obj[key];
}).join('&');

Et l'équivalent ES2017: (merci à Lukas)

let str = Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&');

Remarque: vous souhaiterez probablement l'utiliser encodeURIComponent()si les clés / valeurs ne sont pas encodées en URL.


50
Je ne changerais que+ encodeURIComponent(obj[key])
Jacob Valenta

1
@JacobValenta qui ne fait pas partie de la question
benweet

5
Le voici dans ES2015Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas

2
Cela se décompose si l'objet a des propriétés imbriquées.
Sean the Bean

2
Pour encoder la réponse =${encodeURIComponent(val)}
ES2015

53

Que dis-tu de ça? C'est une ligne et aucune dépendance:

new URLSearchParams(obj).toString();
// OUT: param1=something&param2=somethingelse&param3=another&param4=yetanother

Utilisez-le avec l'URL intégrée comme suit:

let obj = { param1: 'something', param2: 'somethingelse', param3: 'another' }
obj['param4'] = 'yetanother';
const url = new URL(`your_url.com`);
url.search = new URLSearchParams(obj);
const response = await fetch(url);

[Edit 4 avril 2020]: comme mentionné dans les commentaires, les nullvaleurs seront interprétées comme une chaîne 'null'. Soyez donc prudent avec les valeurs nulles.


10
c'est la meilleure réponse d'un mile
hraban

Remarque: dans le nœud <10, vous devrez importer / exiger, par exemple,const { URLSearchParams } = require("url");
groovecoder

1
@HonsaStunna Je n'ai jamais pensé à mettre des objets multidimensionnels sur un paramètre d'URL, utilisez généralement POST avec JSON pour cela
jfunk

1
Soyez prudent avec celui-ci et les valeurs nulles.
Xaraxia le

26

Approche ES2017

Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')

1
ES2017 , techniquement.
Nick Zalutskiy

1
cela nécessite un encodage de la clé et de la valeur. voir les autres réponses re encodeURIComponent.
hraban le

24

ES6:

function params(data) {
  return Object.keys(data).map(key => `${key}=${encodeURIComponent(data[key])}`).join('&');
}

console.log(params({foo: 'bar'}));
console.log(params({foo: 'bar', baz: 'qux$'}));


19

Pour un niveau de profondeur ...

var serialiseObject = function(obj) {
    var pairs = [];
    for (var prop in obj) {
        if (!obj.hasOwnProperty(prop)) {
            continue;
        }
        pairs.push(prop + '=' + obj[prop]);
    }
    return pairs.join('&');
}

jsFiddle .

On parlait d'une fonction récursive pour des objets arbitrairement profonds ...

var serialiseObject = function(obj) {
    var pairs = [];
    for (var prop in obj) {
        if (!obj.hasOwnProperty(prop)) {
            continue;
        }
        if (Object.prototype.toString.call(obj[prop]) == '[object Object]') {
            pairs.push(serialiseObject(obj[prop]));
            continue;
        }
        pairs.push(prop + '=' + obj[prop]);
    }
    return pairs.join('&');
}

jsFiddle .

Cela signifie bien sûr que le contexte d'imbrication est perdu dans la sérialisation.

Si les valeurs ne sont pas encodées en URL au départ et que vous avez l'intention de les utiliser dans une URL, consultez JavaScript encodeURIComponent().


1
alex: Désolé, nous sommes fermés ...; o)
user113716

+1 pour être plus sûr que je suis prêt à être: .hasOwnProperty(prop).
user113716

c'est génial - juste besoin d'un niveau pour l'instant, mais la fonction récursive est bonne à avoir. Merci de l'ajouter!
bobsoap

1
@ ripper234 Vous êtes libre de ne pas utiliser cette méthode, si elle répond à vos besoins.
alex le

6
Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&')

5

Pour mémoire et au cas où vous auriez un navigateur prenant en charge ES6, voici une solution avec reduce:

Object.keys(obj).reduce((prev, key, i) => (
  `${prev}${i!==0?'&':''}${key}=${obj[key]}`
), '');

Et voici un extrait en action!

// Just for test purposes
let obj = {param1: 12, param2: "test"};

// Actual solution
let result = Object.keys(obj).reduce((prev, key, i) => (
  `${prev}${i!==0?'&':''}${key}=${obj[key]}`
), '');

// Run the snippet to show what happens!
console.log(result);


4

Depuis que j'ai fait tellement de choses sur une fonction récursive, voici ma propre version.

function objectParametize(obj, delimeter, q) {
    var str = new Array();
    if (!delimeter) delimeter = '&';
    for (var key in obj) {
        switch (typeof obj[key]) {
            case 'string':
            case 'number':
                str[str.length] = key + '=' + obj[key];
            break;
            case 'object':
                str[str.length] = objectParametize(obj[key], delimeter);
        }
    }
    return (q === true ? '?' : '') + str.join(delimeter);
}

http://jsfiddle.net/userdude/Kk3Lz/2/


3
Juste quelques pensées aléatoires (a) []est préférable à new Array()(b) Vous pouvez utiliser delimiter = delimiter || '&';pour l'argument default (et vous l'avez mal orthographié) (c) Itérer avec for ( in )va itérer sur toutes les propriétés énumérables, y compris les choses sur la chaîne de prototypes (se obj.hasOwnProperty()défend contre cela) (d) typeofpeut mentir sur ce que sont les choses, par exemple, certains nombres peuvent être Objectsi construits avec le Number()constructeur (e) Arrayont une push()méthode pour ajouter des membres (f) en comparant à trueest redondant. Je suis un salaud pointilleux mais vous vouliez des commentaires! :)
alex

1
... et si vous pensez que Crockford a raison sur tout, vous ne devriez pas laisser tomber les boîtiers d'interrupteurs. Je ne suis pas d’accord avec lui sur ce point. : D
alex

@alex - Je l'apprécie. a) J'avais ça au début, il était tard et j'avais sommeil; b) je ne sais pas quelle est l'amélioration, le second était également un moment de manque de sommeil; c) Je me demandais pourquoi vous avez utilisé hasOwnProperty(); d) c'est certainement vrai et c'est un bon point; e) Je ne me suis jamais habitué à l'utilisation push()ou aux pop()méthodes; f) breakou non break, telle est la question. Merci pour votre contribution détaillée. :)
Jared Farrish

4

Un code utile lorsque vous avez le tableau dans votre requête:

var queryString = Object.keys(query).map(key => {
    if (query[key].constructor === Array) {
        var theArrSerialized = ''
        for (let singleArrIndex of query[key]) {
            theArrSerialized = theArrSerialized + key + '[]=' + singleArrIndex + '&'
        }
        return theArrSerialized
    }
    else {
        return key + '=' + query[key] + '&'
    }
}
).join('');
console.log('?' + queryString)

4

Si vous utilisez NodeJS 13.1 ou supérieur, vous pouvez utiliser le module de chaîne de requête natif pour analyser les chaînes de requête.

const qs = require('querystring');
let str = qs.stringify(obj)

3
var str = '';

for( var name in obj ) {
    str += (name + '=' + obj[name] + '&');
}

str = str.slice(0,-1);

Donnez-moi une chance.

Exemple: http://jsfiddle.net/T2UWT/


1
@Jared: Moins efficace qu'une boucle.
user113716

@Jared: nom d'utilisateur non nécessaire sous les questions et réponses. Les notifications sont automatiques.
user113716

"Moins performant"? Si je passe un objet arbitraire et que je souhaite qu'une chaîne GET soit renvoyée, qu'est-ce que les performances ont à voir avec cela?
Jared Farrish le

@Jared: Je suis désolé, mais peut-être que je ne comprends pas votre sens. Si vous suggérez l'utilisation d'appels de fonction récursifs plutôt qu'une boucle, alors je suis tout à fait certain que les fonctions récursives fonctionneront plus lentement que la boucle. Ai-je mal compris votre point?
user113716

1
Eh bien, je ne sais pas, je suppose. S'il y avait une fonction que vous lui envoyez un objet et produisez une version qualifiée GET concaténée de chaîne de cet objet, je n'imagine pas qu'une seule boucle traitera toujours l'entrée. Bien sûr, une boucle à un seul niveau "surpassera" toujours une récursion à plusieurs niveaux, mais une boucle à un seul niveau ne gérera même pas un objet à plusieurs niveaux, IMO.
Jared Farrish le

3

Vous pouvez utiliser la paramméthode de jQuery :

var obj = {
  param1: 'something',
  param2: 'somethingelse',
  param3: 'another'
}
obj['param4'] = 'yetanother';
var str = jQuery.param(obj);
alert(str);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


3
new URLSearchParams({hello: 'world', foo: 'bar' }).toString()

semble faire le travail. Pas besoin de encodeURIComponent. Les sortieshello=world&foo=bar


1
Ceci est une réponse en double à celle que j'ai publiée.
jfunk

2

Si vous avez besoin d'une fonction récursive qui produira des paramètres d'URL appropriés en fonction de l'objet donné, essayez ma fonction Coffee-Script.

@toParams = (params) ->
    pairs = []
    do proc = (object=params, prefix=null) ->
      for own key, value of object
        if value instanceof Array
          for el, i in value
            proc(el, if prefix? then "#{prefix}[#{key}][]" else "#{key}[]")
        else if value instanceof Object
          if prefix?
            prefix += "[#{key}]"
          else
            prefix = key
          proc(value, prefix)
        else
          pairs.push(if prefix? then "#{prefix}[#{key}]=#{value}" else "#{key}=#{value}")
    pairs.join('&')

ou le JavaScript compilé ...

toParams = function(params) {
  var pairs, proc;
  pairs = [];
  (proc = function(object, prefix) {
    var el, i, key, value, _results;
    if (object == null) object = params;
    if (prefix == null) prefix = null;
    _results = [];
    for (key in object) {
      if (!__hasProp.call(object, key)) continue;
      value = object[key];
      if (value instanceof Array) {
        _results.push((function() {
          var _len, _results2;
          _results2 = [];
          for (i = 0, _len = value.length; i < _len; i++) {
            el = value[i];
            _results2.push(proc(el, prefix != null ? "" + prefix + "[" + key + "][]" : "" + key + "[]"));
          }
          return _results2;
        })());
      } else if (value instanceof Object) {
        if (prefix != null) {
          prefix += "[" + key + "]";
        } else {
          prefix = key;
        }
        _results.push(proc(value, prefix));
      } else {
        _results.push(pairs.push(prefix != null ? "" + prefix + "[" + key + "]=" + value : "" + key + "=" + value));
      }
    }
    return _results;
  })();
  return pairs.join('&');
};

Cela construira des chaînes comme ceci:

toParams({a: 'one', b: 'two', c: {x: 'eight', y: ['g','h','j'], z: {asdf: 'fdsa'}}})

"a=one&b=two&c[x]=eight&c[y][0]=g&c[y][1]=h&c[y][2]=j&c[y][z][asdf]=fdsa"

Je pense que je vais l'accrocher au mur
pie6k

2

Une approche fonctionnelle.

var kvToParam = R.mapObjIndexed((val, key) => {
  return '&' + key + '=' + encodeURIComponent(val);
});

var objToParams = R.compose(
  R.replace(/^&/, '?'),
  R.join(''),
  R.values,
  kvToParam
);

var o = {
  username: 'sloughfeg9',
  password: 'traveller'
};

console.log(objToParams(o));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>


1
Object.toparams = function ObjecttoParams(obj) 
{
  var p = [];
  for (var key in obj) 
  {
    p.push(key + '=' + encodeURIComponent(obj[key]));
  }
  return p.join('&');
};

0

cette méthode utilise la récursivité pour descendre dans la hiérarchie d'objets et générer des paramètres de style de rails que les rails interprètent comme des hachages incorporés. objToParams génère une chaîne de requête avec une esperluette supplémentaire à la fin, et objToQuery supprime l'esperluette finale.

 function objToQuery(obj){
  let str = objToParams(obj,'');
  return str.slice(0, str.length);
}
function   objToParams(obj, subobj){
  let str = "";

   for (let key in obj) {
     if(typeof(obj[key]) === 'object') {
       if(subobj){
         str += objToParams(obj[key], `${subobj}[${key}]`);
       } else {
         str += objToParams(obj[key], `[${key}]`);
       }

     } else {
       if(subobj){
         str += `${key}${subobj}=${obj[key]}&`;
       }else{
         str += `${key}=${obj[key]}&`;
       }
     }
   }
   return str;
 }

0

Vous pouvez utiliser npm lib query-string

const queryString = require('query-string');

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// Returns 'foo=bar&baz=qux&baz=quux&corge='

0

const obj = { id: 1, name: 'Neel' };
let str = '';
str = Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&');
console.log(str);

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.