Як серіалізувати Об'єкт у списку параметрів URL-запиту?


148

Не знаючи клавіш JavaScript Object, як я можу перетворити щось на кшталт ...

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

obj[param4] = 'yetanother';

... в ...

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

...?


Шукаєте рекурсивного рішення?
Джаред Фарріш

1
@Jared Я додав рекурсивне рішення :)
alex

@alex - Спасибі; Мені подобається бачити відповіді більш досвідченого люду на складніші проблеми. :)
Джаред Фарріш

2
@Jared Ви знаєте, я ніколи не думаю про себе як про досвідченого розробника JavaScript. Більше, як рубати, поки це працює хлопець :)
alex

@alex - О так, і я. Але як би порівняти те, що ви зібрали, з тим, як я б підійшов до цього? Я постійно вражений.
Джаред Фарріш

Відповіді:


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

Приклад: http://jsfiddle.net/WFPen/


3
Чому б не використовувати функцію з рекурсією?
Джаред Фарріш

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

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

3
Чи не повинен obj [key] бути загорнутим у encodeURIComponent ()? Що станеться, якщо "somethingelse" було "щось та інше"?
James S

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, яка також буде працювати, яка виводить функціонуючий парам-метод з 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, це не частина питання
benweet

5
Ось це в ES2015Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Лукас

2
Це руйнується, якщо об’єкт має якісь вкладені властивості.
Sean the Bean

2
Для кодування відповіді ES2015 змініть на:=${encodeURIComponent(val)}
BBlackwo

49

Як щодо цього? Це один рядок і ніяких залежностей:

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

25

ES2017 підхід

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

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

1
для цього потрібно кодування як ключа, так і значення. див. інші відповіді про кодуванняURIComponent.
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)
користувач113716

+1 за те , що безпечніше , ніж я готовий бути: .hasOwnProperty(prop).
користувач113716

це чудово - просто зараз потрібен 1 рівень, але рекурсивну функцію добре мати. Дякуємо, що додали його!
bobsoap

1
@ ripper234 Ви не можете використовувати цей метод, якщо він відповідає вашим вимогам.
alex


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 || '&';для аргументу за замовчуванням (і ви написали це неправильно) (c) Ітерація з ітерацією for ( in )буде перерахована над усіма переліченими властивостями, включаючи речі в прототипі ланцюга ( obj.hasOwnProperty()захищається від цього) (г) typeofможе брехати про те, що є, наприклад, деякі числа можуть бути, Objectякщо у конструкторі з Number()конструктором (е) Arrayє push()метод додавання членів (f) порівняння із trueзайвим. Я дурний гад, але ви хотіли відгуку! :)
alex

1
... і якщо ви вирішите, що Крокфорд має рацію у всьому, вам не слід дозволяти випадкам перемикання. Я з цим не згоден. : D
alex

@alex - я ціную це. а) у мене було це спочатку, було пізно, і я спав; б) не впевнений, що таке поліпшення, другий - це теж момент, який не був уві сні; в) мені було цікаво, чому ти використовував hasOwnProperty(); г) це, безумовно, правда і хороший момент; д) я ніколи не звик до використання push()чи pop()методів; f) 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: Менш ефективний, ніж цикл.
користувач113716

@Jared: ім'я користувача не потрібно нижче Qs та As. Повідомлення проходять автоматично.
користувач113716

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

@Jred: Вибачте, але, можливо, я не розумію вашого значення. Якщо ви пропонуєте використовувати рекурсивні виклики функцій, а не циклічні, то я впевнений, що рекурсивні функції будуть виконуватись повільніше, ніж циклічні. Я неправильно зрозумів вашу думку?
користувач113716

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"

Я думаю, я
повішу

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

Цей метод використовує рекурсію для спуску в ієрархію об'єктів та генерування парамлерів стилю рейок, які інтерфейси інтерпретують як вбудовані хеші. 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 query-string

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.