Как сериализовать объект в список параметров запроса 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';

...?

bobsoap
источник
Вы ищете рекурсивное решение?
Джаред 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/

aroth
источник
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 для автономности.
Уэс
129

Если вы используете jQuery, это то, что он использует для параметризации параметров запроса GET ajax:

$.param( obj )

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

герцог
источник
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.

benweet
источник
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'. Так что будьте осторожны с нулевыми значениями.

jfunk
источник
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('&')
Lukas
источник
1
ES2017 , технически.
Ник Залуцкий
1
это требует кодирования как ключа, так и значения. см. другие ответы 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$'}));

Великобритания
источник
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 Вы можете не использовать этот метод, если он соответствует вашим требованиям.
Алекс
6
Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&')
Генри
источник
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)
Robilol
источник
3
var str = '';

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

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

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

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

user113716
источник
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>

Slex
источник
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"
Zac
источник
Я думаю, что я повешу это на стене
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>

tladuke
источник
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='
Майкл
источник
0

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

Нил Ратод
источник