Jak serializować obiekt do listy parametrów zapytania URL?

148

Nie znając kluczy JavaScript Object, jak mogę włączyć coś takiego ...

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

obj[param4] = 'yetanother';

...w...

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

...?

bobsoap
źródło
Szukasz rozwiązania rekurencyjnego?
Jared Farrish
1
@Jared Dodałem rozwiązanie rekurencyjne :)
alex
@alex - Dzięki; Lubię widzieć odpowiedzi bardziej doświadczonych ludzi na bardziej skomplikowane problemy. :)
Jared Farrish,
2
@Jared Wiesz, nigdy tak naprawdę nie uważałem się za doświadczonego programistę JavaScript. Bardziej jak hack 'til it works guy :)
alex
@alex - O tak, ja też. Ale jak porównać to, co stworzyłeś, z tym, jak bym do tego podszedł? Ciągle jestem zdumiony.
Jared Farrish,

Odpowiedzi:

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

Przykład: http://jsfiddle.net/WFPen/

aroth
źródło
3
Dlaczego nie użyć funkcji z rekurencją?
Jared Farrish,
1
dzięki @aroth! Zaakceptowałem powyższą odpowiedź @ patrick zamiast twojej (są w zasadzie takie same), ponieważ, jak sądzę, był pierwszy. Jestem bardzo wdzięczny za odpowiedź.
bobsoap
4
@bobsoap: Myślę, że @aroth trochę mnie wyprzedził, więc dałbym @arothowi znak, że wszystkie inne rzeczy są równe.
user113716,
3
Czy obj [klucz] nie powinien być zawijany w encodeURIComponent ()? Co się stanie, jeśli „coś innego” to „coś innego”?
James S
1
Jestem prawie pewien, że nie powinna to być akceptowana odpowiedź lub prawie każda odpowiedź w tym wątku przepełnienia stosu. Głównym powodem jest to, że żaden z nich, z wyjątkiem możliwej odpowiedzi @ zac poniżej, nie zapewni prawidłowego zakodowania tego obiektu. { 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?" };Wygląda na to, że @UnLoCo zasugerował bibliotekę NPM, która również będzie działać, która wyciąga działającą metodę param z jQuery, aby była samodzielna.
Wes
120

Elegancki: (zakładając, że korzystasz z nowoczesnej przeglądarki lub węzła)

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

I odpowiednik ES2017: (dzięki Lukasowi)

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

Uwaga: prawdopodobnie zechcesz użyć, encodeURIComponent()jeśli klucze / wartości nie są zakodowane w postaci adresu URL.

benweet
źródło
50
Zmieniłbym tylko+ encodeURIComponent(obj[key])
Jacob Valenta
1
@JacobValenta to nie jest część pytania
benweet
5
Tutaj jest w ES2015Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas
2
To się psuje, jeśli obiekt ma zagnieżdżone właściwości.
Sean the Bean
2
Aby zakodować odpowiedź ES2015, zmień na:=${encodeURIComponent(val)}
BBlackwo
49

Co powiesz na to? To jedna linia i brak zależności:

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

Użyj go z wbudowanym adresem URL w następujący sposób:

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

[Edit April 4, 2020]: jak wspomniano w komentarzach, nullwartości będą interpretowane jako ciąg 'null'. Dlatego uważaj na wartości null.

śmieci
źródło
10
to najlepsza odpowiedź o milę
hraban
Uwaga: w węźle <10 musisz zaimportować / wymagać np.const { URLSearchParams } = require("url");
groovecoder
1
@HonsaStunna Nigdy nie myślałem o umieszczaniu wielowymiarowych obiektów w parametrze adresu URL, zwykle używam do tego POST z JSON
jfunk
1
Uważaj na ten jeden i wartości null.
Xaraxia
25

Podejście ES2017

Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas
źródło
1
ES2017 technicznie.
Nick Zalutskiy
1
wymaga to zakodowania klucza i wartości. zobacz inne odpowiedzi ponownie 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$'}));

uk
źródło
19

Na jeden poziom głębokości ...

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 .

Mówiono o funkcji rekurencyjnej dla dowolnie głębokich obiektów ...

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 .

To oczywiście oznacza, że ​​kontekst zagnieżdżenia jest tracony podczas serializacji.

Jeśli wartości nie są zakodowane w adresie URL od początku i zamierzasz użyć ich w adresie URL, sprawdź JavaScript encodeURIComponent().

Alex
źródło
1
alex: Przepraszamy, zamknięte ...; o)
user113716
+1 za to bezpieczniejsze niż jestem gotów być: .hasOwnProperty(prop).
user113716
to świetnie - na razie potrzebujesz tylko 1 poziomu, ale dobrze jest mieć funkcję rekurencyjną. Dzięki za dodanie!
bobsoap
1
@ ripper234 Możesz nie używać tej metody, jeśli odpowiada Twoim wymaganiom.
Alex
6
Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&')
Henz
źródło
5

Tak dla przypomnienia, jeśli masz przeglądarkę obsługującą ES6, oto rozwiązanie z reduce:

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

A oto fragment w akcji!

// 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
źródło
4

Ponieważ zrobiłem wiele rzeczy na temat funkcji rekurencyjnej, oto moja własna wersja.

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
źródło
3
Tylko niektóre przypadkowe myśli (a) []są preferowane zamiast new Array()(b) Możesz użyć delimiter = delimiter || '&';argumentu default (i źle go przeliterowałeś) (c) Iterowanie z for ( in )will iteruje po wszystkich wyliczalnych właściwościach, w tym w łańcuchu prototypów ( obj.hasOwnProperty()chroni przed tym) (d) typeofmoże kłamać na temat tego, czym są rzeczy, np. niektóre liczby mogą być, Objectjeśli zostały skonstruowane za pomocą Number()konstruktora (e) Arraymają push()metodę dodawania elementów (f) w porównaniu do truejest zbędna. Jestem nieznośnym draniem, ale chciałeś poznać opinię! :)
alex
1
... a jeśli uważasz, że Crockford ma rację co do wszystkiego, nie pozwól, aby przypadkowe przełączniki upadły. Jednak nie zgadzam się z nim w tej kwestii. : D
alex
@alex - doceniam to. a) Miałem to na początku, było późno i byłem śpiący; b) nie jestem pewien, jaka jest poprawa, druga była również momentem pozbawionym snu; c) Zastanawiałem się, dlaczego używasz hasOwnProperty(); d) to z pewnością prawda i słuszna uwaga; e) Nigdy nie przyzwyczaiłem się do używania push()lub pop()metod; f) breakczy nie break, oto jest pytanie. Dziękuję za szczegółowe informacje. :)
Jared Farrish
4

Przydatny kod, gdy masz tablicę w zapytaniu:

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
źródło
4

Jeśli używasz NodeJS 13.1 lub wyższego, możesz użyć natywnego modułu querystring do analizowania ciągów zapytań.

const qs = require('querystring');
let str = qs.stringify(obj)
Robilol
źródło
3
var str = '';

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

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

Daj temu szansę.

Przykład: http://jsfiddle.net/T2UWT/

user113716
źródło
1
@Jared: Mniej wydajne niż pętla.
user113716
@Jared: nazwa użytkownika niepotrzebna poniżej Q i As. Powiadomienia są automatyczne.
user113716
„Mniej wydajne”? Jeśli przekażę dowolny obiekt i chcę, aby został zwrócony ciąg znaków GET, co ma z tym wspólnego wydajność?
Jared Farrish
@Jared: Przepraszam, ale być może nie rozumiem twojego znaczenia. Jeśli sugerujesz użycie rekurencyjnych wywołań funkcji zamiast pętli, jestem pewien, że funkcje rekurencyjne będą działać wolniej niż pętla. Czy źle zrozumiałem twój punkt widzenia?
user113716
1
Cóż, chyba nie wiem. Gdyby istniała funkcja, którą wysyłasz do obiektu i wyprowadzasz połączoną ze stringami wersję tego obiektu z kwalifikacją GET, nie wyobrażam sobie, by pojedyncza pętla zawsze zajmowała się wejściem. Oczywiście pętla jednopoziomowa zawsze „przewyższa” rekursję wielopoziomową, ale pętla jednopoziomowa nie obsłuży nawet obiektu wielopoziomowego, IMO.
Jared Farrish
3

Możesz użyć parammetody 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
źródło
3
new URLSearchParams({hello: 'world', foo: 'bar' }).toString()

wydaje się wykonywać swoją pracę. Nie ma potrzeby encodeURIComponent. Wyjściahello=world&foo=bar

Munkel Trogg
źródło
1
To jest duplikat odpowiedzi na tę, którą opublikowałem.
jfunk
2

Jeśli potrzebujesz funkcji rekurencyjnej, która będzie generować odpowiednie parametry adresu URL na podstawie podanego obiektu, wypróbuj moją wersję 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('&')

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

To skonstruuje napisy w ten sposób:

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
źródło
Chyba
powiesię
2

Podejście funkcjonalne.

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

ta metoda używa rekurencji, aby zejść do hierarchii obiektów i wygenerować parametry w stylu rails, które railsy interpretują jako osadzone hashe. obiekt objToParams generuje ciąg zapytania z dodatkowym ampersandem na końcu, a objToQuery usuwa ostatni znak amperseand.

 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;
 }
jedna jednostka
źródło
0

Możesz użyć ciągu zapytania npm lib

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

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// Returns 'foo=bar&baz=qux&baz=quux&corge='
Michael
źródło
0

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

Neel Rathod
źródło