Como serializar um objeto em uma lista de parâmetros de consulta de URL?

148

Sem conhecer as chaves de um JavaScript Object, como posso ativar algo como ...

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

obj[param4] = 'yetanother';

...para dentro...

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

...?

bobsoap
fonte
Você está procurando uma solução recursiva?
Jared Farrish
1
@Jared I adicionou-se uma solução recursiva :)
Alex
@alex - Obrigado; Gosto de ver as respostas das pessoas mais experientes sobre os problemas mais complicados. :)
Jared Farrish
2
@ Jared Sabe, eu nunca me considero um desenvolvedor JavaScript experiente . Mais como hack 'til ele funciona cara :)
alex
@alex - Ah sim, eu também. Mas como o que você montou se compara com o que eu teria abordado? Estou constantemente espantado.
Jared Farrish

Respostas:

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

Exemplo: http://jsfiddle.net/WFPen/

aroth
fonte
3
Por que não usar uma função com recursão?
Jared Farrish
1
obrigado @aroth! Eu só aceitei a resposta de @ patrick acima da sua (elas são essencialmente as mesmas) porque ele foi o primeiro, acredito. Sou muito grato por sua resposta.
bobsoap
4
@bobsoap: Eu acho que @aroth estava um pouco à minha frente, então eu daria a @aroth o sinal de que todas as outras coisas eram iguais.
precisa saber é o seguinte
3
A obj [key] não deve ser encapsulada em encodeURIComponent ()? O que acontece se 'somethingelse' for 'something & else'?
James S
1
Tenho certeza de que essa não deve ser a resposta aceita ou quase todas as respostas nesse segmento de stackoverflow. O principal motivo é que nenhum deles, com exceção da possível resposta do @ zac abaixo, satisfará a codificação adequada desse objeto. { 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?" };Parece que o @UnLoCo sugeriu uma biblioteca NPM que também funcionará, o que puxa o método param de funcionamento do jQuery para ser autônomo.
Wes
128

Se você usa jQuery, é isso que ele usa para parametrizar as opções de uma solicitação GET ajax:

$.param( obj )

http://api.jquery.com/jQuery.param/

Duque
fonte
120

Um elegante: (supondo que você esteja executando um navegador ou nó moderno)

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

E o equivalente ao ES2017: (graças a Lukas)

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

Nota: Você provavelmente deseja usar encodeURIComponent()se as chaves / valores não estiverem codificados em URL.

agridoce
fonte
50
Gostaria apenas de mudar+ encodeURIComponent(obj[key])
Jacob Valenta
1
@JacobValenta que não faz parte da pergunta
benweet
5
Aqui está no ES2015Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas
2
Isso é interrompido se o objeto tiver propriedades aninhadas.
Sean the Bean
2
Para codificar a mudança ES2015 resposta a:=${encodeURIComponent(val)}
BBlackwo
49

Que tal agora? É uma linha e sem dependências:

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

Use-o com o URL incorporado da seguinte maneira:

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);

[Editar em 4 de abril de 2020]: conforme mencionado nos nullvalores dos comentários , será interpretado como uma sequência 'null'. Portanto, tenha cuidado com valores nulos.

jfunk
fonte
10
esta é a melhor resposta por uma milha
hraban 11/11/19
Nota: no nó <10 você vai precisar para importação / exigem, por exemplo,const { URLSearchParams } = require("url");
groovecoder
1
@HonsaStunna Eu nunca pensei sobre a colocação de objetos multidimensionais em um parâmetro de URL, costumo usar POST com JSON para isso
Jfunk
1
Tenha cuidado com este e valores nulos.
Xaraxia 03/04
25

Abordagem ES2017

Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas
fonte
1
ES2017 , tecnicamente.
Nick Zalutskiy
1
isso precisa de codificação de chave e valor. veja outras respostas re encodeURIComponent.
hraban 11/09/19
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$'}));

Reino Unido
fonte
19

Para um nível profundo ...

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 .

Falou-se sobre uma função recursiva para objetos arbitrariamente profundos ...

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 .

Obviamente, isso significa que o contexto de aninhamento é perdido na serialização.

Se os valores não forem codificados para URLs e você pretende usá-los em uma URL, consulte JavaScript encodeURIComponent().

alex
fonte
1
alex: Desculpe, estamos fechados ...; o)
user113716
+1 por ser mais seguro do que eu estou disposto a ser: .hasOwnProperty(prop).
precisa saber é o seguinte
isso é ótimo - só precisamos de 1 nível por enquanto, mas é bom ter a função recursiva. Obrigado por adicioná-lo!
bobsoap
1
@ ripper234 Você é livre para não usar esse método, se ele atender às suas necessidades.
alex
6
Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&')
Henry
fonte
5

Apenas para o registro e caso você tenha um navegador compatível com ES6, aqui está uma solução com reduce:

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

E aqui está um trecho em ação!

// 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);

Yan Foto
fonte
4

Desde que fiz uma grande coisa sobre uma função recursiva, aqui está minha própria versão.

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/

Jared Farrish
fonte
3
Apenas alguns pensamentos aleatórios (a) []são preferidos a new Array()(b) Você pode usar delimiter = delimiter || '&';para o argumento padrão (e você o digitou errado) (c) A for ( in )iteração com iterará sobre todas as propriedades enumeráveis, incluindo itens na cadeia de protótipos ( obj.hasOwnProperty()defende isso) (d) typeofpode mentir sobre o que são as coisas, por exemplo, alguns números podem ser Objectse construídos com o Number()construtor (e) Arraytiverem um push()método para adicionar membros (f) em comparação com a trueredundância. Eu sou um bastardo, mas você queria feedback! :)
alex
1
... e se você acha que Crockford está certo sobre tudo, não deve deixar passar os casos de troca. Mas eu discordo dele. : D
alex
@alex - eu aprecio isso. a) Eu tive isso no começo, já era tarde e estava com sono; b) não tendo certeza de qual é a melhora, o segundo também foi um momento de privação de sono; c) Fiquei me perguntando por que você usou hasOwnProperty(); d) isso certamente é verdade e um bom argumento; e) eu nunca tinha chegado uso de usar push()ou pop()métodos; f) breakou não break, eis a questão. Obrigado por sua contribuição detalhada. :)
Jared Farrish
4

Um código útil quando você tem a matriz em sua consulta:

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)
Pouya Jabbarisani
fonte
4

Se você estiver usando o NodeJS 13.1 ou superior, poderá usar o módulo de querystring nativo para analisar as strings de consulta.

const qs = require('querystring');
let str = qs.stringify(obj)
Robilol
fonte
3
var str = '';

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

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

Dê uma chance a isso.

Exemplo: http://jsfiddle.net/T2UWT/

user113716
fonte
1
@ Jared: Menos eficiente que um loop.
user113716
@ Jared: nome de usuário não necessário abaixo de Qs e As. As notificações são automáticas.
user113716
"Menos desempenho"? Se eu passar um objeto arbitrário e desejar que uma string GET seja retornada, o que o desempenho tem a ver com isso?
Jared Farrish
@ Jared: Sinto muito, mas talvez eu não esteja entendendo o seu significado. Se você está sugerindo o uso de chamadas de função recursivas em vez de um loop, tenho certeza de que as funções recursivas terão um desempenho mais lento que o loop. Eu entendi mal o seu ponto?
user113716
1
Bem, eu não sei, eu acho. Se houvesse uma função que você enviasse a ele um objeto e emitisse uma versão GET concatenada por string desse objeto, não imagino que um único loop sempre lide com a entrada. Obviamente, um loop de nível único sempre "supera" uma recursão de vários níveis, mas um loop de nível único nem sequer lida com um objeto de vários níveis, o IMO.
Jared Farrish
3

Você pode usar o parammétodo do 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>

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

parece fazer o trabalho. Não há necessidade encodeURIComponent. Saídashello=world&foo=bar

Munkel Trogg
fonte
1
Esta é uma resposta duplicada para a que eu publiquei.
Jfunk #
2

Se você precisar de uma função recursiva que produza parâmetros de URL adequados com base no objeto fornecido, experimente o meu 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 o JavaScript compilado ...

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('&');
};

Isso construirá seqüências de caracteres como estas:

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"
Zac
fonte
Acho que vou pendurá-lo na parede
pie6k 30/01
2

Uma abordagem funcional.

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>

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

esse método usa recursão para descer para a hierarquia de objetos e gerar parâmetros de estilo de trilhos, que os interpretam como hashes incorporados. objToParams gera uma string de consulta com um e comercial extra no final, e objToQuery remove o e comercial final.

 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;
 }
uma unidade
fonte
0

Você poderia usar 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='
Michael
fonte
0

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

Neel Rathod
fonte