Как сериализовать объект в список параметров запроса URL?


149

Не зная ключей JavaScript Object, как я могу превратить что-то вроде ...

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

obj[param4] = 'yetanother';

...в...

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

...?


Вы ищете рекурсивное решение?
Джаред Farrish

1
@Jared я добавил рекурсивное решение :)
Àlex

@alex - Спасибо; Мне нравится видеть ответы более опытных людей на более сложные проблемы. :)
Джаред Фарриш

2
@Jared Вы знаете, я никогда не думал о себе, как о опытном разработчике JavaScript. Больше как взломать «сезам он работает парень :)
Алекс

@alex - Да, я тоже. Но как сравнить то, что вы собрали, с тем, как я бы подошел к этому? Я постоянно поражен.
Джаред Фарриш

Ответы:


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

Пример: http://jsfiddle.net/WFPen/


3
Почему бы не использовать функцию с рекурсией?
Джаред Фарриш

1
спасибо @aroth! Я только принял ответ @ Патрика выше вашего (они, по сути, такие же), потому что он был первым, я полагаю. Я очень благодарен за ваш ответ.
bobsoap

4
@bobsoap: Я думаю, что @aroth немного опередил меня, поэтому я бы поставил галочку @aroth, при прочих равных условиях.
user113716

3
Разве obj [key] не должен быть заключен в encodeURIComponent ()? Что произойдет, если «что-то еще» было «чем-то еще»?
Джеймс С

1
Я почти уверен, что это не должен быть принятый ответ, или почти каждый ответ в этом потоке stackoverflow. Основная причина, по которой ни один из них, за исключением возможного ответа @ zac, приведенного ниже, удовлетворит правильную кодировку этого объекта. { 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?" };Похоже, @UnLoCo предложил библиотеку NPM, которая также будет работать, которая вытягивает функционирующий метод param из jQuery для автономности.
Уэс


120

Элегантный: (при условии, что вы используете современный браузер или узел)

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

И эквивалент ES2017: (спасибо Лукасу)

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

Примечание: вы, вероятно, захотите использовать, encodeURIComponent()если ключи / значения не закодированы в URL.


50
Я бы только изменился+ encodeURIComponent(obj[key])
Джейкоб Валента

1
@JacobValenta, который не является частью вопроса
Бенвит

5
Вот это в ES2015Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Лукас

2
Это ломается, если у объекта есть какие-либо вложенные свойства.
Шон Боб

2
Для кодирования ответа ES2015 измените на:=${encodeURIComponent(val)}
BBlackwo

53

Как насчет этого? Это одна строка и никаких зависимостей:

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

Используйте его со встроенным URL следующим образом:

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

[Изменить 4 апреля 2020 года]: как указано в комментариях, nullзначения будут интерпретироваться как строка 'null'. Так что будьте осторожны с нулевыми значениями.


10
это лучший ответ на милю
Hraban

Примечание: в узле <10 вам необходимо импортировать / требовать, например,const { URLSearchParams } = require("url");
groovecoder

1
@HonsaStunna Я никогда не думал о размещении многомерных объектов в параметре URL, обычно для этого использую POST с JSON
jfunk

1
Будьте осторожны с этим и нулевыми значениями.
Xaraxia

26

ES2017 подход

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

1
ES2017 , технически.
Ник Залуцкий

1
это требует кодирования как ключа, так и значения. см. другие ответы re encodeURIComponent.
hraban


19

Для одного уровня глубоко ...

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 .

Речь шла о рекурсивной функции для сколь угодно глубоких объектов ...

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 .

Это, конечно, означает, что контекст вложенности теряется при сериализации.

Если значения не являются URL-адресами для начала и вы собираетесь использовать их в URL-адресе, ознакомьтесь с JavaScript encodeURIComponent().


1
alex: Извините, мы закрыты ...; o)
user113716

+1 за то, что безопаснее, чем я хочу .hasOwnProperty(prop).
user113716

это здорово - сейчас нужен только 1 уровень, но рекурсивная функция полезна. Спасибо за добавление!
bobsoap

1
@ ripper234 Вы можете не использовать этот метод, если он соответствует вашим требованиям.
Алекс


5

Просто для справки, и если у вас есть браузер, поддерживающий ES6, вот решение с reduce:

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

И вот фрагмент в действии!

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

Так как я сделал так много о рекурсивной функции, вот моя собственная версия.

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
Просто некоторые случайные мысли (а) []предпочтительнее, чем new Array()(б) Вы можете использовать delimiter = delimiter || '&';для аргумента по умолчанию (и вы написали его неправильно) (в) Итерирование с for ( in )будет перебирать все перечисляемые свойства, включая вещи в цепочке прототипов ( obj.hasOwnProperty()защищает от этого) (d) typeofможет лгать о том, что есть, например, некоторые числа могут быть Objectпостроены с помощью Number()конструктора (e) Arrayиметь push()метод для добавления членов (f) по сравнению с trueизбыточным. Я придирчивый ублюдок, но вы хотели получить обратную связь! :)
Alex

1
... и если ты считаешь, что Крокфорд прав во всем, не позволяй переключаться между делами. Я не согласен с ним в этом, хотя. : D
alex

@alex - я ценю это. а) у меня было это сначала, было поздно, и я был сонным; б) не уверен, что улучшение, второй был также лишенный сна момент; в) мне было интересно, почему вы использовали hasOwnProperty(); г) это, безусловно, правда и хорошая мысль; д) я никогда не привык к использованию push()или pop()методам; е) breakили нет break, вот в чем вопрос. Спасибо за ваш подробный вклад. :)
Джаред Фарриш

4

Полезный код, когда у вас есть массив в вашем запросе:

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

Если вы используете NodeJS 13.1 или выше, вы можете использовать собственный модуль строки запроса для анализа строк запроса.

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

Дайте этому шанс.

Пример: http://jsfiddle.net/T2UWT/


1
@Jared: менее эффективен, чем цикл.
user113716

@Jared: имя пользователя не нужно ниже Qs и As. Уведомления автоматические.
user113716

"Менее производительный"? Если я передаю произвольный объект и хочу, чтобы была возвращена строка GET, какое отношение имеет производительность?
Джаред Фарриш

@Jared: извините, но, возможно, я не понимаю вашего значения. Если вы предлагаете использовать рекурсивные вызовы функций, а не цикл, то я совершенно уверен, что рекурсивные функции будут выполняться медленнее, чем цикл. Я неправильно понял вашу точку зрения?
user113716

1
Ну, я не знаю, наверное. Если бы существовала функция, которая отправляет объекту и выдает GET-квалифицированную версию этого объекта с конкатенацией строк, я не думаю, что один цикл всегда будет иметь дело с вводом. Конечно, одноуровневый цикл всегда будет «превосходить» многоуровневую рекурсию, но одноуровневый цикл даже не будет обрабатывать многоуровневый объект, IMO.
Джаред Фарриш

3

Вы можете использовать paramметод 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()

кажется, делает работу. Нет необходимости для encodeURIComponent. Выходыhello=world&foo=bar


1
Это дублирующий ответ на тот, который я написал.
Jfunk

2

Если вам нужна рекурсивная функция, которая будет генерировать правильные параметры URL в зависимости от заданного объекта, попробуйте мой 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('&')

или JavaScript скомпилирован ...

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

Это создаст строки примерно так:

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"

Я думаю, что я повешу это на стене
pie6k

2

Функциональный подход.

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

этот метод использует рекурсию для спуска в иерархию объектов и генерирует параметры стиля rails, которые rails интерпретирует как встроенные хэши. objToParams генерирует строку запроса с дополнительным амперсандом в конце, а objToQuery удаляет последний амперсанд и.

 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

Вы можете использовать строку запроса npm lib

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.