Javascript 对象的查询字符串编码

Posted

技术标签:

【中文标题】Javascript 对象的查询字符串编码【英文标题】:Query-string encoding of a Javascript Object 【发布时间】:2010-12-15 10:43:36 【问题描述】:

您知道将 javascript 对象编码为 string 的快速简单的方法吗?我可以通过 GET 请求传递它?

没有 jQuery,没有其他框架 - 只是普通的 Javascript :)

【问题讨论】:

如果有适合您的解决方案,为什么 JQuery 不能成为解决方案? @eaglei22 因为当时我正在为 IPTV 机顶盒设备做一个项目,并且不允许使用外部库。 ;-) 感谢您的回复。我不时地看到这个规范,并且总是想知道为什么。嗯,现在我有一个,谢谢! :) @eaglei22 因为有时您不想加载大型库以通过 id 获取一个元素。 现在大部分浏览器都支持URLSearchParams... 【参考方案1】:

喜欢这样吗?

serialize = function(obj) 
  var str = [];
  for (var p in obj)
    if (obj.hasOwnProperty(p)) 
      str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
    
  return str.join("&");


console.log(serialize(
  foo: "hi there",
  bar: "100%"
));
// foo=hi%20there&bar=100%25

编辑:这个也转换递归对象(使用php“数组”表示法作为查询字符串)

serialize = function(obj, prefix) 
  var str = [],
    p;
  for (p in obj) 
    if (obj.hasOwnProperty(p)) 
      var k = prefix ? prefix + "[" + p + "]" : p,
        v = obj[p];
      str.push((v !== null && typeof v === "object") ?
        serialize(v, k) :
        encodeURIComponent(k) + "=" + encodeURIComponent(v));
    
  
  return str.join("&");


console.log(serialize(
  foo: "hi there",
  bar: 
    blah: 123,
    quux: [1, 2, 3]
  
));
// foo=hi%20there&bar%5Bblah%5D=123&bar%5Bquux%5D%5B0%5D=1&bar%5Bquux%5D%5B1%5D=2&bar%5Bquux%5D%5B2%5D=3

【讨论】:

不会破坏给定 foo: [1,2,3], bar: "100%" 吗? @Ofri:对于设置为接收它的服务器的 POST 请求,JSON 是一个不错的选择。对于 GET 请求,如果您向服务器发送的不是一些简单的参数,那么很可能您的设计是错误的。 @Marcel 那是因为该函数不检查 hasOwnProperty。我已经更新了你的小提琴,所以现在可以了:jsfiddle.net/rudiedirkx/U5Tyb/1 @TimDown 关于您在 GET 请求中发送简单参数的评论。我不同意。将参数分组到数组中可能会变得很方便,因为服务器端的 PHP 会找到一个稳定的关联数组。我不明白为什么这是错误的设计。 'if (obj.hasOwnProperty(prop))' 有必要吗? for in 语句循环刚好覆盖对象的属性,因此调用 hasOwnProperty 始终评估为 true【参考方案2】:

只需使用URLSearchParams 这个works in all current browsers

new URLSearchParams(object).toString()

【讨论】:

不,因为它不做递归对象 不适用于嵌套对象。 let j = m: 5, n: k: 1 ; new URLSearchParams(j).toString(); // result "m=5&n=%5Bobject+Object%5D" @EddieMongeJr 查询字符串在设计上是键值对,您甚至不应该序列化嵌套对象这个答案是现代的方式。需要点赞。 是的,它们是键值对,但没有任何内容表明该值不能是字符串编码的对象。此外,原始问题要求“将 Javascript 对象转换为字符串”,它可以具有嵌套属性 @EddieMongeJr 甚至被接受的答案(以及其他简单的答案)也不支持嵌套对象。你可以stringify嵌套对象之前可以URLSearchParams【参考方案3】:

jQuery 有一个函数,jQuery.param(),如果你已经在使用它,你可以使用它: http://api.jquery.com/jquery.param/

示例:

var params =  width:1680, height:1050 ;
var str = jQuery.param( params );

str 现在包含 width=1680&height=1050

【讨论】:

引用 Napolux(OP):“只是普通的 Javascript”。 :P jQuery.param() 具有险恶的行为。尝试执行 var a = [];一个[2564] = 12; console.log(jQuery.param( propertylist: a ));看看我的意思。 @akond jQuery 文档明确指出您不能传入裸数组。 @Ariel 他没有传入一个裸数组。他传入了一个在索引 2564 处只有一个值的数组。为了演示:var a = []; a[5] = 'foo'; jQuery.param( parameters: a ); 结果为"parameters[]=&parameters[]=&parameters[]=&parameters[]=&parameters[]=&parameters[]=foo"。这虽然是正确的,但可能不是您所期望的。 问题特意问过Vanilla JS【参考方案4】:

我建议使用URLSearchParams接口:

const searchParams = new URLSearchParams();
const params = foo: "hi there", bar: "100%" ;
Object.keys(params).forEach(key => searchParams.append(key, params[key]));
console.log(searchParams.toString())

或者通过像这样将搜索对象传递给构造函数:

const params = foo: "hi there", bar: "100%" ;
const queryString = new URLSearchParams(params).toString();
console.log(queryString);

【讨论】:

有趣的建议,但请注意,浏览器对此功能的支持仍然非常不完整。 如果你不支持 IE(现在很常见)和一些特定的移动版本,这是最好的答案,因为它是纯 JavaScript。 @bmaggi 不适用于嵌套属性 a: 1: 'test', 2: 'test2' 预期:a[1]=test&a[2]=test2 @bravemaster 这是一个很棒的解决方案,尤其是对于节点开发人员。谢谢! 请注意,如今,在现代环境中,如果您的起点是上述对象,则可以使用 Object.entriesconst searchParams = new URLSearchParams(Object.entries(params));【参考方案5】:
Object.keys(obj).reduce(function(a,k)a.push(k+'='+encodeURIComponent(obj[k]));return a,[]).join('&')

编辑:我喜欢这个单行,但我敢打赌,如果它在语义上与接受的答案相匹配,那将是一个更受欢迎的答案:

function serialize( obj ) 
    let str = '?' + Object.keys(obj).reduce(function(a, k)
        a.push(k + '=' + encodeURIComponent(obj[k]));
        return a;
    , []).join('&');
    return str;

【讨论】:

减少功能的专线将大大提高可读性。 使用 .map() 而不是 .reduce() 会更简单:Object.keys(obj).map(k => k + '=' + encodeURIComponent(obj[k])).join('&') 请注意Object.keys 仅适用于 IE >= 9 使用 ES6 模板而不是串联进一步改进 @Jannes 代码 - Object.keys(obj).map(k => `$k=$encodeURIComponent(obj[k])`).join('&') 如果您使用比serialize 更通用的术语作为编辑的函数名称,则此答案会更受欢迎,也许是encodeAsQueryString。否则,每个人都必须重命名它以供实际使用——或者更糟的是,不要重命名它。【参考方案6】:

这是 ES6 中的单行代码:

Object.keys(obj).map(k => `$encodeURIComponent(k)=$encodeURIComponent(obj[k])`).join('&');

【讨论】:

用 k 替换钥匙,你就是金子 警告!这仅适用于浅层物体。如果您有一个***属性是另一个对象,则这一行将输出“key=%5Bobject%20Object%5D”。就像提醒一样。 另外,这不会吐出数组。我得到了export?actions[]=finance,create,edit,而它应该有export?actions[]=finance&actions[]=create&actions[]=edit,这是糟糕的标准。 数组几乎总是“你自己做”,因为就规范而言,URL 参数只是字符串,所以你可以做任何不是您正在调用的服务器可以正确读取单个字符串。 actions[] 是 PHP 表示法; Django 使用多个action 代替(没有[] 后缀);其他一些 ORM/CMS 需要逗号分隔的列表等。所以“如果它不是简单的字符串,首先要确保你知道你的服务器甚至想要什么”。 非常优雅的解决方案!【参考方案7】:

使用 Node.js v6.6.3

const querystring = require('querystring')

const obj = 
  foo: 'bar',
  baz: 'tor'


let result = querystring.stringify(obj)
// foo=bar&baz=tor

参考:https://nodejs.org/api/querystring.html

【讨论】:

这不应该被 IMO 否决,如果它是服务器上的 JS,这应该是正确的答案。 好像不支持嵌套对象。 @MichaelBenin 为什么你认为这仅适用于节点服务器?你检查了吗?【参考方案8】:

对 user187291 接受的解决方案的一个小修改:

serialize = function(obj) 
   var str = [];
   for(var p in obj)
       if (obj.hasOwnProperty(p)) 
           str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
       
   
   return str.join("&");

检查对象上的 hasOwnProperty 使 JSLint/JSHint 很高兴,并且如果对象不是简单的字典,它可以防止意外地序列化对象的方法或其他东西。请参阅本页中有关声明的段落:http://javascript.crockford.com/code.html

【讨论】:

【参考方案9】:

Rails / PHP 样式查询生成器

此方法将 Javascript 对象转换为 URI Query String。还处理嵌套数组和对象(在Rails / PHP 语法中):

function serializeQuery(params, prefix) 
  const query = Object.keys(params).map((key) => 
    const value  = params[key];

    if (params.constructor === Array)
      key = `$prefix[]`;
    else if (params.constructor === Object)
      key = (prefix ? `$prefix[$key]` : key);

    if (typeof value === 'object')
      return serializeQuery(value, key);
    else
      return `$key=$encodeURIComponent(value)`;
  );

  return [].concat.apply([], query).join('&');


示例用法:

let params = 
  a: 100,
  b: 'has spaces',
  c: [1, 2, 3],
  d:  x: 9, y: 8


serializeQuery(params)
// returns 'a=100&b=has%20spaces&c[]=1&c[]=2&c[]=3&d[x]=9&d[y]=8

【讨论】:

很好的例子。我在你的回答中修正了一个错字。顺便说一句,如果您编辑 function 以排除 falsy 值(null、undefined、NaN、'')会很有趣... 这是一个很好的例子来解决这个问题,写得很好,并结合了解决这个问题所需的递归和类型检查。 我会投票给这个答案而不是其他答案,因为这是正确实现递归的唯一解决方案。【参考方案10】:

好吧,似乎每个人都把他的单线放在这里,所以我的:

const encoded = Object.entries(obj).map(([k, v]) => `$k=$encodeURIComponent(v)`).join("&");

【讨论】:

Object.entries 在 IE 中不受支持。 @MBouwman 当然,IE 已经超越了善恶,所以你必须使用 babel/core-js @chpio Babel/core-js 不支持 Object.entries 如果我是对的。 core 支持 Object.entries:github.com/zloirock/core-js/blob/master/… 甚至旧的 corejs2 babel 运行时转换也支持它github.com/babel/babel/blob/…【参考方案11】:

您需要发送任意对象吗?如果是这样,GET 是个坏主意,因为用户代理和 Web 服务器将接受的 URL 长度是有限的。我的建议是建立一个名称-值对数组来发送,然后建立一个查询字符串:

function QueryStringBuilder() 
    var nameValues = [];

    this.add = function(name, value) 
        nameValues.push( name: name, value: value );
    ;

    this.toQueryString = function() 
        var segments = [], nameValue;
        for (var i = 0, len = nameValues.length; i < len; i++) 
            nameValue = nameValues[i];
            segments[i] = encodeURIComponent(nameValue.name) + "=" + encodeURIComponent(nameValue.value);
        
        return segments.join("&");
    ;


var qsb = new QueryStringBuilder();
qsb.add("veg", "cabbage");
qsb.add("vegCount", "5");

alert( qsb.toQueryString() );

【讨论】:

【参考方案12】:

这是接受答案的咖啡脚本版本。这可能会为某人节省时间。

serialize = (obj, prefix) ->
  str = []
  for p, v of obj
    k = if prefix then prefix + "[" + p + "]" else p
    if typeof v == "object"
      str.push(serialize(v, k))
    else
      str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v))

  str.join("&")

【讨论】:

谢谢阿方索!真的节省了我的时间!【参考方案13】:

稍微好看一点

objectToQueryString(obj, prefix) 
    return Object.keys(obj).map(objKey => 
        if (obj.hasOwnProperty(objKey)) 
            const key = prefix ? `$prefix[$objKey]` : objKey;
            const value = obj[objKey];

            return typeof value === "object" ?
                this.objectToQueryString(value, key) :
                `$encodeURIComponent(key)=$encodeURIComponent(value)`;
        

        return null;
    ).join("&");

【讨论】:

【参考方案14】:

这是带有Object.entries 的简洁递归版本。它处理任意嵌套的数组,但不处理嵌套对象。它还删除了空元素:

const format = (k,v) => v !== null ? `$k=$encodeURIComponent(v)` : ''

const to_qs = (obj) => 
    return [].concat(...Object.entries(obj)
                       .map(([k,v]) => Array.isArray(v) 
                          ? v.map(arr => to_qs([k]:arr)) 
                          : format(k,v)))
           .filter(x => x)
           .join('&');

例如:

let json =  
    a: [1, 2, 3],
    b: [],              // omit b
    c: 1,
    d: "test&encoding", // uriencode
    e: [[4,5],[6,7]],   // flatten this
    f: null,            // omit nulls
    g: 0
;

let qs = to_qs(json)

=> "a=1&a=2&a=3&c=1&d=test%26encoding&e=4&e=5&e=6&e=7&g=0"

【讨论】:

这个版本在处理嵌套数组时为我完成了这项工作。稍作调整以使用 Ruby/PHP 样式的数组键,但效果很好。【参考方案15】:

这个跳过空值/未定义值

export function urlEncodeQueryParams(data) 
    const params = Object.keys(data).map(key => data[key] ? `$encodeURIComponent(key)=$encodeURIComponent(data[key])` : '');
    return params.filter(value => !!value).join('&');

【讨论】:

【参考方案16】:

在 ES7 中,您可以在一行中编写:

const serialize = (obj) => (Object.entries(obj).map(i => [i[0], encodeURIComponent(i[1])].join('=')).join('&'))

【讨论】:

【参考方案17】:

单行将对象转换为查询字符串,以防有人再次需要它

let Objs =  a: 'obejct-a', b: 'object-b' 

Object.keys(objs).map(key => key + '=' + objs[key]).join('&')

// result will be a=object-a&b=object-b

【讨论】:

【参考方案18】:

我有一个更简单的解决方案,它不使用任何第三方库,并且已经适用于任何具有“Object.keys”的浏览器(也就是所有现代浏览器 + edge + ie):

在 ES5 中

function(a)
    if( typeof(a) !== 'object' ) 
        return '';
    return `?$Object.keys(a).map(k=>`$k=$a[k]`).join('&')`;

在 ES3 中

function(a)
    if( typeof(a) !== 'object' ) 
        return '';
    return '?' + Object.keys(a).map(function(k) return k + '=' + a[k] ).join('&');

【讨论】:

【参考方案19】:

用于对 JAVASCRIPT 对象进行查询字符串编码的 ES6 解决方案

const params = 
  a: 1,
  b: 'query stringify',
  c: null,
  d: undefined,
  f: '',
  g:  foo: 1, bar: 2 ,
  h: ['Winterfell', 'Westeros', 'Braavos'],
  i:  first:  second:  third: 3 


static toQueryString(params = , prefix) 
  const query = Object.keys(params).map((k) => 
    let key = k;
    const value = params[key];

    if (!value && (value === null || value === undefined || isNaN(value))) 
      value = '';
    

    switch (params.constructor) 
      case Array:
        key = `$prefix[]`;
        break;
      case Object:
        key = (prefix ? `$prefix[$key]` : key);
        break;
    

    if (typeof value === 'object') 
      return this.toQueryString(value, key); // for nested objects
    

    return `$key=$encodeURIComponent(value)`;
  );

  return query.join('&');

toQueryString(参数)

"a=1&b=query%20stringify&c=&d=&f=&g[foo]=1&g[bar]=2&h[]=Winterfell&h[]=Westeros&h[]=Braavos&i[first][second][third]=3"

【讨论】:

不适用于对象数组,例如:["a": 1, "b": [1,2]] :(【参考方案20】:

如果您想递归地转换嵌套对象,并且该对象可能包含也可能不包含数组(并且数组可能包含对象或数组等),那么解决方案会变得更复杂一些。这是我的尝试。

我还添加了一些选项来选择是否要为每个对象成员记录它位于主对象中的深度,以及选择是否要为来自转换数组的成员添加标签。

理想情况下,您应该测试 thing 参数是否真的接收到对象或数组。

function thingToString(thing,maxDepth,recordLevel,markArrays)
    //thing: object or array to be recursively serialized
    //maxDepth (int or false):
    // (int) how deep to go with converting objects/arrays within objs/arrays
    // (false) no limit to recursive objects/arrays within objects/arrays
    //recordLevel (boolean):
    //  true - insert "(level 1)" before transcript of members at level one (etc)
    //  false - just 
    //markArrays (boolean):
    //  insert text to indicate any members that came from arrays
    var result = "";
    if (maxDepth !== false && typeof maxDepth != 'number') maxDepth = 3;
    var runningDepth = 0;//Keeps track how deep we're into recursion

    //First prepare the function, so that it can call itself recursively
    function serializeAnything(thing)
        //Set path-finder values
        runningDepth += 1;
        if(recordLevel)result += "(level " + runningDepth + ")";

        //First convert any arrays to object so they can be processed
        if (thing instanceof Array)
            var realObj = ;var key;
            if (markArrays) realObj['type'] = "converted array";
            for (var i = 0;i < thing.length;i++)
                if (markArrays) key = "a" + i; else key = i;
                realObj[key] = thing[i];
            
            thing = realObj;
            console.log('converted one array to ' + typeof realObj);
            console.log(thing);
        

        //Then deal with it
        for (var member in thing)
            if (typeof thing[member] == 'object' && runningDepth < maxDepth)
                serializeAnything(thing[member]);
                //When a sub-object/array is serialized, it will add one to
                //running depth. But when we continue to this object/array's
                //next sibling, the level must go back up by one
                runningDepth -= 1;
             else if (maxDepth !== false && runningDepth >= maxDepth) 
                console.log('Reached bottom');
             else 
            if (
                typeof thing[member] == "string" || 
                typeof thing[member] == 'boolean' ||
                typeof thing[member] == 'number'
            )
                result += "(" + member + ": " + thing[member] + ") ";
              else 
                result += "(" + member + ": [" + typeof thing[member] + " not supported]) ";
            
        
    
    //Actually kick off the serialization
    serializeAnything(thing);

    return result;


【讨论】:

感谢递归方法【参考方案21】:

添加可接受的解决方案,这适用于对象和对象数组:

parseJsonAsQueryString = function (obj, prefix, objName) 
    var str = [];
    for (var p in obj) 
        if (obj.hasOwnProperty(p)) 
            var v = obj[p];
            if (typeof v == "object") 
                var k = (objName ? objName + '.' : '') + (prefix ? prefix + "[" + p + "]" : p);
                str.push(parseJsonAsQueryString(v, k));
             else 
                var k = (objName ? objName + '.' : '') + (prefix ? prefix + '.' + p : p);
                str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
                //str.push(k + "=" + v);
            
        
    
    return str.join("&");

如果您使用像 asp.net mvc 操作方法中的对象参数,还添加了 objName。

【讨论】:

【参考方案22】:

我发了comparison of JSON stringifiers,结果如下:

JSON:    "_id":"5973782bdb9a930533b05cb2","isActive":true,"balance":"$1,446.35","age":32,"name":"Logan Keller","email":"logankeller@artiq.com","phone":"+1 (952) 533-2258","friends":["id":0,"name":"Colon Salazar","id":1,"name":"French Mcneil","id":2,"name":"Carol Martin"],"favoriteFruit":"banana"
Rison:   (_id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller@artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258')
O-Rison: _id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller@artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258'
JSURL:   ~(_id~'5973782bdb9a930533b05cb2~isActive~true~balance~'!1*2c446.35~age~32~name~'Logan*20Keller~email~'logankeller*40artiq.com~phone~'*2b1*20*28952*29*20533-2258~friends~(~(id~0~name~'Colon*20Salazar)~(id~1~name~'French*20Mcneil)~(id~2~name~'Carol*20Martin))~favoriteFruit~'banana)
QS:      _id=5973782bdb9a930533b05cb2&isActive=true&balance=$1,446.35&age=32&name=Logan Keller&email=logankeller@artiq.com&phone=+1 (952) 533-2258&friends[0][id]=0&friends[0][name]=Colon Salazar&friends[1][id]=1&friends[1][name]=French Mcneil&friends[2][id]=2&friends[2][name]=Carol Martin&favoriteFruit=banana
URLON:   $_id=5973782bdb9a930533b05cb2&isActive:true&balance=$1,446.35&age:32&name=Logan%20Keller&email=logankeller@artiq.com&phone=+1%20(952)%20533-2258&friends@$id:0&name=Colon%20Salazar;&$id:1&name=French%20Mcneil;&$id:2&name=Carol%20Martin;;&favoriteFruit=banana
QS-JSON: isActive=true&balance=%241%2C446.35&age=32&name=Logan+Keller&email=logankeller%40artiq.com&phone=%2B1+(952)+533-2258&friends(0).id=0&friends(0).name=Colon+Salazar&friends(1).id=1&friends(1).name=French+Mcneil&friends(2).id=2&friends(2).name=Carol+Martin&favoriteFruit=banana

其中最短的是URL Object Notation。

【讨论】:

【参考方案23】:

这是一个适用于 .NET 后端的开箱即用解决方案。我已经接受了这个线程的主要答案并对其进行了更新以适应我们的 .NET 需求。

function objectToQuerystring(params) 
var result = '';

    function convertJsonToQueryString(data, progress, name) 
        name = name || '';
        progress = progress || '';
        if (typeof data === 'object') 
            Object.keys(data).forEach(function (key) 
                var value = data[key];
                if (name == '') 
                    convertJsonToQueryString(value, progress, key);
                 else 
                    if (isNaN(parseInt(key))) 
                        convertJsonToQueryString(value, progress, name + '.' + key);
                     else 
                        convertJsonToQueryString(value, progress, name + '[' + key+ ']');
                    
                
            )
         else 
            result = result ? result.concat('&') : result.concat('?');
            result = result.concat(`$name=$data`);
        
    

    convertJsonToQueryString(params);
    return result;

【讨论】:

【参考方案24】:

似乎直到现在还没有人提到另一个流行的库 qs。你可以添加它

$ yarn add qs

然后像这样使用它

import qs from 'qs'

const array =  a:  b: 'c'  
const stringified = qs.stringify(array,  encode: false )

console.log(stringified) //-- outputs a[b]=c

【讨论】:

那是因为 OP 想使用纯 javascript,没有外部库。【参考方案25】:

以更好的方式做到这一点。

它可以处理像a=val&amp;b[0]=val&amp;b[1]=val&amp;c=val&amp;d[some key]=val这样的STANDARD查询形式的递归对象或数组,这是最终的功能。

逻辑、功能

const objectToQueryString = (initialObj) => 
  const reducer = (obj, parentPrefix = null) => (prev, key) => 
    const val = obj[key];
    key = encodeURIComponent(key);
    const prefix = parentPrefix ? `$parentPrefix[$key]` : key;

    if (val == null || typeof val === 'function') 
      prev.push(`$prefix=`);
      return prev;
    

    if (['number', 'boolean', 'string'].includes(typeof val)) 
      prev.push(`$prefix=$encodeURIComponent(val)`);
      return prev;
    

    prev.push(Object.keys(val).reduce(reducer(val, prefix), []).join('&'));
    return prev;
  ;

  return Object.keys(initialObj).reduce(reducer(initialObj), []).join('&');
;

示例

const testCase1 = 
  name: 'Full Name',
  age: 30


const testCase2 = 
  name: 'Full Name',
  age: 30,
  children: [
    name: 'Child foo',
    name: 'Foo again'
  ],
  wife: 
    name: 'Very Difficult to say here'
  


console.log(objectToQueryString(testCase1));
console.log(objectToQueryString(testCase2));

现场测试

展开下面的sn-p,在浏览器中验证结果-

const objectToQueryString = (initialObj) => 
  const reducer = (obj, parentPrefix = null) => (prev, key) => 
    const val = obj[key];
    key = encodeURIComponent(key);
    const prefix = parentPrefix ? `$parentPrefix[$key]` : key;

    if (val == null || typeof val === 'function') 
      prev.push(`$prefix=`);
      return prev;
    

    if (['number', 'boolean', 'string'].includes(typeof val)) 
      prev.push(`$prefix=$encodeURIComponent(val)`);
      return prev;
    

    prev.push(Object.keys(val).reduce(reducer(val, prefix), []).join('&'));
    return prev;
  ;

  return Object.keys(initialObj).reduce(reducer(initialObj), []).join('&');
;

const testCase1 = 
  name: 'Full Name',
  age: 30


const testCase2 = 
  name: 'Full Name',
  age: 30,
  children: [
    name: 'Child foo',
    name: 'Foo again'
  ],
  wife: 
    name: 'Very Difficult to say here'
  


console.log(objectToQueryString(testCase1));
console.log(objectToQueryString(testCase2));

需要考虑的事项。

它会跳过functionsnullundefined 的值 对于 空对象数组,它会跳过 keysvalues 它不处理 NumberStringnew Number(1)new String('my string') 制作的对象,因为没有人应该这样做

【讨论】:

也许将“if (val == null”更改为“if (val === null”(===)。但这很好用。【参考方案26】:

如果您有很多嵌套对象,上述答案将不起作用。 相反,您可以从这里选择函数参数 - https://github.com/knowledgecode/jquery-param/blob/master/jquery-param.js 对我来说效果很好!

    var param = function (a) 
    var s = [], rbracket = /\[\]$/,
        isArray = function (obj) 
            return Object.prototype.toString.call(obj) === '[object Array]';
        , add = function (k, v) 
            v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v;
            s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v);
        , buildParams = function (prefix, obj) 
            var i, len, key;

            if (prefix) 
                if (isArray(obj)) 
                    for (i = 0, len = obj.length; i < len; i++) 
                        if (rbracket.test(prefix)) 
                            add(prefix, obj[i]);
                         else 
                            buildParams(prefix + '[' + (typeof obj[i] === 'object' ? i : '') + ']', obj[i]);
                        
                    
                 else if (obj && String(obj) === '[object Object]') 
                    for (key in obj) 
                        buildParams(prefix + '[' + key + ']', obj[key]);
                    
                 else 
                    add(prefix, obj);
                
             else if (isArray(obj)) 
                for (i = 0, len = obj.length; i < len; i++) 
                    add(obj[i].name, obj[i].value);
                
             else 
                for (key in obj) 
                    buildParams(key, obj[key]);
                
            
            return s;
        ;

    return buildParams('', a).join('&').replace(/%20/g, '+');
;

【讨论】:

【参考方案27】:

好的,这是一篇较旧的帖子,但我正面临这个问题,我找到了我的个人解决方案..也许可以帮助其他人..

     function objToQueryString(obj)
        var k = Object.keys(obj);
        var s = "";
        for(var i=0;i<k.length;i++) 
            s += k[i] + "=" + encodeURIComponent(obj[k[i]]);
            if (i != k.length -1) s += "&";
        
        return s;
     ;

【讨论】:

【参考方案28】:

我为此编写了一个包:object-query-string :)

支持嵌套对象、数组、自定义编码函数等。轻量级和 jQuery 免费。

// TypeScript
import  queryString  from 'object-query-string';

// Node.js
const  queryString  = require("object-query-string");

const query = queryString(
    filter: 
        brands: ["Audi"],
        models: ["A4", "A6", "A8"],
        accidentFree: true
    ,
    sort: 'mileage'
);

返回

filter[brands][]=Audi&filter[models][]=A4&filter[models][]=A6&filter[models][]=A8&filter[accidentFree]=true&sort=milage

【讨论】:

【参考方案29】:

在这里查看了一些热门答案后,我编写了另一个解决一些极端情况的实现

function serialize(params, prefix)                 
    return Object.entries(params).reduce((acc, [key, value]) => 
        // remove whitespace from both sides of the key before encoding
        key = encodeURIComponent(key.trim());

        if (params.constructor === Array ) 
          key = `$prefix[]`;
         else if (params.constructor === Object) 
          key = (prefix ? `$prefix[$key]` : key);
        

        /**
         *  - undefined and NaN values will be skipped automatically
         *  - value will be empty string for functions and null
         *  - nested arrays will be flattened
         */
        if (value === null || typeof value === 'function') 
            acc.push(`$key=`);
         else if (typeof value === 'object') 
            acc = acc.concat(serialize(value, key));
         else if(['number', 'boolean', 'string'].includes(typeof value) && value === value)  // self-check to avoid NaN
            acc.push(`$key=$encodeURIComponent(value)`);
        

        return acc;
    , []);


function objectToQueryString(queryParameters) 
    return queryParameters ? serialize(queryParameters).join('&'): '';


let x = objectToQueryString(
    foo: 'hello world',
    bar: 
      blah: 123,
      list: [1, 2, 3],
        'nested array': [[4,5],[6,7]] // will be flattened
    ,
    page: 1,
    limit: undefined, // field will be ignored
    check: false,
    max: NaN, // field will be ignored
    prop: null,
    ' key value': 'with spaces' // space in key will be trimmed out
);
  
console.log(x); // foo=hello%20world&bar[blah]=123&bar[list][]=1&bar[list][]=2&bar[list][]=3&bar[nested%20array][][]=4&bar[nested%20array][][]=5&bar[nested%20array][][]=6&bar[nested%20array][][]=7&page=1&check=false&prop=&key%20value=with%20spaces

【讨论】:

【参考方案30】:

PHP 表示法的 Typescript 版本(无 url 转义版本)

/**
 * Converts an object into a Cookie-like string.
 * @param toSerialize object or array to be serialized
 * @param prefix used in deep objects to describe the final query parameter
 * @returns ampersand separated key=value pairs
 *
 * Example:
 * ```js
 * serialize(hello:[world: "nice"]); // outputs  "hello[0][world]=nice"
 * ```
 * ---
 * Adapted to TS from a *** answer https://***.com/a/1714899/4537906
 */
const serialize = (toSerialize: unknown = , prefix?: string) => 
  const keyValuePairs = [];

  Object.keys(toSerialize).forEach((attribute) => 
    if (Object.prototype.hasOwnProperty.call(toSerialize, attribute)) 
      const key = prefix ? `$prefix[$attribute]` : attribute;
      const value = toSerialize[attribute];
      const toBePushed =
        value !== null && typeof value === "object"
          ? serialize(value, key)
          : `$key=$value`;
      keyValuePairs.push(toBePushed);
    
  );

  return keyValuePairs.join("&");
;

【讨论】:

以上是关于Javascript 对象的查询字符串编码的主要内容,如果未能解决你的问题,请参考以下文章

2018-07-17 Base64解码与编码

如何使用 java 对从 javascript 到 servletpage 的查询字符串进行编码和解码?

第4章 编码表

将 Javascript 对象编码为 Json 字符串

JavaScript 全局对象

Laravel jsonb 列在 javascript 对象中编码为字符串