如何将对象序列化为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';

...?


您是否正在寻找递归解决方案?
Jared Farrish'7

1
@Jared我添加了一个递归解决方案:)
Alex

@alex-谢谢;我喜欢看到经验丰富的人对更复杂问题的回答。:)
贾里德·法瑞什

2
@Jared您知道,我从来没有真正把自己当作一个经验丰富的 JavaScript开发人员。更像hack'til it works guy :)
alex

@alex-哦,是的,我也是。但是,您将这些内容与我所采用的方法相比如何?我一直感到惊讶。
Jared Farrish

Answers:


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打勾。
user113716 2011年

3
obj [key]是否应该包装在encodeURIComponent()中?如果“ somethingelse”是“ something&else”会怎样?
詹姆斯·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库也可以正常工作,该库将起作用的param方法从jQuery中拉出来是独立的。
Wes


120

一个优雅的例子:(假设您正在运行现代的浏览器或节点)

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

和ES2017等效:(感谢Lukas)

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

注意:encodeURIComponent()如果键/值未经过URL编码,则可能要使用。


50
我只会改变+ encodeURIComponent(obj[key])
Jacob Valenta 2015年

1
@JacobValenta并不是问题的一部分
benweet 2015年

5
这就是ES2015Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&')
Lukas

2
如果对象具有任何嵌套属性,则此操作将失败。
肖恩·比恩

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

[编辑2020年4月4日]:如注释中所述,null值将被解释为字符串'null'。因此,请注意使用空值。


10
这是最好的答案
-hraban

注意:在节点<10中,您需要导入/要求,例如const { URLSearchParams } = require("url");
凹槽编码器,

1
@HonsaStunna我从未想过将多维对象放在URL参数上,通常为此使用JSON的POST
jfunk

1
请小心使用此一和null值。
Xaraxia


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
亚历克斯:抱歉,我们已经关闭...; o)
113716 2011年

+1比我愿意的安全.hasOwnProperty(prop)
user113716 2011年

很棒-暂时只需要1级,但是递归函数很好。感谢您添加!
bobsoap

1
@ ripper234如果符合您的要求,您可以随意不使用该方法。
Alex

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
只是一些随机的想法(a)[]new Array()(b )更可取,您可以将delimiter = delimiter || '&';default用作参数(并且您将其拼写错误)(c)进行迭代for ( in )将对所有可枚举的属性进行迭代,包括原型链上的所有内容(obj.hasOwnProperty()对此进行防御) (d)typeof可以谎言事物是什么,例如,Object如果用Number()构造函数构造(e)Array有一种push()添加成员的方法(f)可以是一些数字,那么这true是多余的。我是个卑鄙的混蛋,但您需要反馈!:)
Alex

1
...如果您认为Crockford在所有事情上都是正确的,那么您就不应该让切换案件陷入困境。我对此表示反对。:D
alex

@alex-非常感谢。a)我起初有这种感觉,已经很晚了,我很困。b)不知道有什么改善,第二个也是睡眠不足的时刻;c)我想知道你为什么使用hasOwnProperty();d)确实是正确的,也是一个好点;e)我从不习惯使用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或更高版本,则可以使用本机querystring模块来解析查询字符串。

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 2011年

@Jared:问与答下方不需要用户名。通知是自动的。
user113716 2011年

“表演较少”?如果我传入一个任意对象,并希望返回GET字符串,那么性能与它有什么关系?
贾里德·法瑞希

@Jared:对不起,但也许我不明白您的意思。如果您建议使用递归函数调用而不是循环,那么我可以肯定的是,递归函数的执行速度将比循环慢。我误会了你的意思吗?
user113716 2011年

1
好吧,我不知道我猜。如果有一个函数向您发送一个对象并输出该对象的字符串连接的GET限定版本,那么我认为没有一个循环将始终处理输入。当然,单级循环将始终“胜过”多级递归,但是单级循环甚至无法处理多级对象IMO。
Jared Farrish

3

您可以使用jQuery的param方法:

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='

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.