¿Cómo serializar un objeto en una lista de parámetros de consulta de URL?

148

Sin conocer las claves de un JavaScript Object, ¿cómo puedo convertir algo como ...

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

obj[param4] = 'yetanother';

...dentro...

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

...?

bobsoap
fuente
¿Estás buscando una solución recursiva?
Jared Farrish
1
@ Jared agregué una solución recursiva :)
alex
@alex - Gracias; Me gusta ver las respuestas de la gente más experimentada sobre los problemas más complicados. :)
Jared Farrish
2
@ Jared Ya sabes, nunca me considero un desarrollador experimentado de JavaScript. Más como hackear hasta que funcione chico :)
alex
@alex - Oh sí, yo también. Pero, ¿cómo se compararía lo que reuniste con cómo lo habría abordado? Estoy constantemente asombrado.
Jared Farrish

Respuestas:

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

Ejemplo: http://jsfiddle.net/WFPen/

Aroth
fuente
3
¿Por qué no usar una función con recursión?
Jared Farrish
1
gracias @aroth! Solo acepté la respuesta de @Patrick sobre la tuya (son esencialmente las mismas) porque él fue el primero, creo. Estoy muy agradecido por tu respuesta.
bobsoap
44
@bobsoap: Creo que @aroth estaba un poco por delante de mí, así que le daría a @aroth la señal de que todas las demás cosas son iguales.
user113716
3
¿No debería obj [key] estar envuelto en encodeURIComponent ()? ¿Qué pasa si 'algo' era 'algo y más'?
James S
1
Estoy bastante seguro de que esta no debería ser la respuesta aceptada, o casi todas las respuestas en este hilo de stackoverflow. La razón principal de no ser ninguno de ellos, con la excepción de la posible respuesta de { 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?" };@zac a continuación, satisfará la codificación adecuada de este objeto. Parece que @UnLoCo sugirió una biblioteca NPM que también funcionará, que extrae el método de param funcional de jQuery para que sea independiente.
Wes
120

Una elegante: (suponiendo que esté ejecutando un navegador o nodo moderno)

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

Y el equivalente de ES2017: (gracias a Lukas)

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

Nota: Probablemente desee usar encodeURIComponent()si las claves / valores no están codificados en URL.

benweet
fuente
50
Solo cambiaría+ encodeURIComponent(obj[key])
Jacob Valenta
1
@JacobValenta que no es parte de la pregunta
benweet 02 de
55
Aquí está en ES2015Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas
2
Esto se desglosa si el objeto tiene propiedades anidadas.
Sean the Bean
2
Para codificar la respuesta de ES2015, cambie a:=${encodeURIComponent(val)}
BBlackwo
52

¿Qué tal esto? Es una línea y no depende:

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

Úselo con la URL incorporada de esta manera:

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 4 de abril de 2020]: como se menciona en los comentarios, los nullvalores se interpretarán como una cadena 'null'. Así que ten cuidado con los valores nulos.

jfunk
fuente
10
esta es la mejor respuesta por una milla
hraban
Nota: en el nodo <10 deberá importar / requerir, por ejemplo,const { URLSearchParams } = require("url");
groovecoder
1
@HonsaStunna Nunca he pensado en poner objetos multidimensionales en un parámetro de URL, generalmente uso POST con JSON para eso
jfunk
1
Tenga cuidado con este y los valores nulos.
Xaraxia
26

Enfoque ES2017

Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas
fuente
1
ES2017 , técnicamente.
Nick Zalutskiy
1
esto necesita codificación de clave y valor. ver otras respuestas re encodeURIComponent.
hraban
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
fuente
19

Para un nivel 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 .

Se habló de una función 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 .

Por supuesto, esto significa que el contexto de anidamiento se pierde en la serialización.

Si los valores no están codificados en URL para comenzar, y tiene la intención de usarlos en una URL, consulte JavaScript encodeURIComponent().

alex
fuente
1
alex: Lo siento, estamos cerrados ...; o)
user113716
1 por ser más seguro de lo que estoy dispuesto a ser: .hasOwnProperty(prop).
user113716
Esto es genial, solo necesito 1 nivel por ahora, pero es bueno tener la función recursiva. Gracias por agregarlo!
bobsoap
1
@ ripper234 Usted es libre de no usar ese método, si se ajusta a sus requisitos.
alex
6
Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&')
Enrique
fuente
5

Solo para el registro y en caso de que tenga un navegador compatible con ES6, aquí hay una solución con reduce:

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

¡Y aquí hay un fragmento en acción!

// 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
fuente
4

Dado que hice tanto alboroto sobre una función recursiva, aquí está mi propia versión.

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
fuente
3
Solo []se prefieren algunos pensamientos aleatorios (a) en lugar de new Array()(b) Puede usar delimiter = delimiter || '&';el argumento predeterminado (y lo deletreó mal) (c) Iterar con for ( in )iterará sobre todas las propiedades enumerables, incluidas las cosas en la cadena del prototipo ( obj.hasOwnProperty()defiende contra esto) (d) typeofpuede mentir acerca de lo que son las cosas, por ejemplo, algunos números pueden ser Objectconstruidos con el Number()constructor (e) si Arrayun push()método para agregar miembros (f) truees redundante. Soy un bastardo quisquilloso pero querías comentarios. :)
alex
1
... y si crees que Crockford tiene razón en todo, no debes dejar que los casos cambien. Aunque no estoy de acuerdo con él en eso. : D
alex
@alex - Te lo agradezco. a) Tenía eso al principio, era tarde y tenía sueño; b) no estoy seguro de cuál es la mejora, el segundo también fue un momento de falta de sueño; c) Me preguntaba por qué lo usabas hasOwnProperty(); d) eso es ciertamente cierto y un buen punto; e) Nunca me he acostumbrado a usar push()o pop()métodos; f) breako no break, esa es la pregunta. Gracias por tu aporte detallado. :)
Jared Farrish
4

Un código útil cuando tiene la matriz en su 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
fuente
4

Si está utilizando NodeJS 13.1 o superior, puede usar el módulo de cadena de consulta nativo para analizar cadenas de consulta.

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

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

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

Dale una oportunidad a esto.

Ejemplo: http://jsfiddle.net/T2UWT/

usuario113716
fuente
1
@Jared: Menos eficiente que un bucle.
user113716
@Jared: el nombre de usuario no es necesario debajo de las preguntas y respuestas. Las notificaciones son automáticas.
user113716
¿"Menos eficiente"? Si paso un objeto arbitrario y quiero que se devuelva una cadena GET, ¿qué tiene que ver el rendimiento con ella?
Jared Farrish
@Jared: Lo siento, pero tal vez no entiendo tu significado. Si está sugiriendo el uso de llamadas a funciones recursivas en lugar de un bucle, entonces estoy bastante seguro de que las funciones recursivas funcionarán más lentamente que el bucle. ¿He entendido mal tu punto?
user113716
1
Bueno, no sé, supongo. Si hubiera una función de enviarle un objeto y generar una versión concatenada de GET de ese objeto, no imagino que un solo bucle siempre se ocupará de la entrada. Por supuesto, un bucle de un solo nivel siempre "superará" a una recursión de varios niveles, pero un bucle de un solo nivel ni siquiera manejará un objeto de varios niveles, IMO.
Jared Farrish
3

Puedes usar el parammétodo 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>

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

Parece hacer el trabajo. No hay necesidad de encodeURIComponent. Salidashello=world&foo=bar

Munkel Trogg
fuente
1
Esta es una respuesta duplicada a la que publiqué.
jfunk
2

Si necesita una función recursiva que produzca los parámetros de URL adecuados en función del objeto proporcionado, pruebe mi 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('&')

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

Esto construirá cadenas de esta manera:

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
fuente
Creo que lo colgaré en la pared
pie6k
2

Un enfoque 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
fuente
1
Object.toparams = function ObjecttoParams(obj) 
{
  var p = [];
  for (var key in obj) 
  {
    p.push(key + '=' + encodeURIComponent(obj[key]));
  }
  return p.join('&');
};
Razi Syed
fuente
0

Este método utiliza la recursividad para descender a la jerarquía de objetos y generar parámetros de estilo de rieles que los rieles interpretan como hashes incrustados. objToParams genera una cadena de consulta con un ampersand adicional al final, y objToQuery elimina el amperseand 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;
 }
una unidad
fuente
0

Podrías 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='
Miguel
fuente
0

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

Neel Rathod
fuente