如何将URL参数转换为JavaScript对象?

454

我有一个像这样的字符串:

abc=foo&def=%5Basf%5D&xyz=5

我怎么能将它转换为这样的JavaScript对象?

{
  abc: 'foo',
  def: '[asf]',
  xyz: 5
}

请参见https://dev59.com/nHNA5IYBdhLWcg3wmfAa。 - Bergi
1
这并不是:https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/get#Example https://developer.mozilla.org/en-US/docs/Web/API/URL/searchParams#Example (尽管我们还需要等待一段时间,让所有浏览器都能够支持它) - Arthur
Object.fromEntries(Array.from(new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5'))) - undefined
34个回答

462

在2021年...请考虑这是过时的。

编辑

根据评论,此次编辑改进和解释了答案。

var search = location.search.substring(1);
JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}')

示例

abc=foo&def=%5Basf%5D&xyz=5解析为五个步骤:

  • decodeURI:abc=foo&def=[asf]&xyz=5
  • 转义引号:相同,因为没有引号
  • 替换 &: abc=foo","def=[asf]","xyz=5
  • 替换 =: abc":"foo","def":"[asf]","xyz":"5
  • 用大括号和引号包围:{"abc":"foo","def":"[asf]","xyz":"5"}

这是合法的 JSON。

一种改进的解决方案允许在搜索字符串中使用更多字符。它使用一个reviver函数进行URI解码:

var search = location.search.substring(1);
JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) })

示例

search = "abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar";

提供

Object {abc: "foo", def: "[asf]", xyz: "5", foo: "b=ar"}

原始答案

一句话概括:

JSON.parse('{"' + decodeURI("abc=foo&def=%5Basf%5D&xyz=5".replace(/&/g, "\",\"").replace(/=/g,"\":\"")) + '"}')

4
为了在CoffeeScript中正常运行,需要在正则表达式中转义'='。 .replace(/=/g,"":"") - airlok
312
那不是一句简短的话...那是个太空站。 - Ziggy
8
最好使用JSON.parse('{"' + decodeURI(location.search.substring(1).replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}'),该代码将解析从URL查询参数中检索到的字符串,并将其转换为JavaScript对象。 - Daniël Tulp
7
如果要解析的 URL 中包含等号字符,那么这种方法就会失效。例如:"cookie=dlksdlfj=sodkfjhsdlfj"。 - jholloman
5
当您拥有一个没有值的参数时,它也不起作用。 - Sych
显示剩余19条评论

360

2023 ES6/7/8及以后的方法

从ES6开始,JavaScript提供了几种构造方式来解决这个问题。

其中包括使用URLSearchParams迭代器

let params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
params.get("abc"); // "foo"

如果您的使用情况需要将其实际转换为对象,您可以实现以下函数:
function paramsToObject(entries) {
  const result = {}
  for(const [key, value] of entries) { // each 'entry' is a [key, value] tupple
    result[key] = value;
  }
  return result;
}

基本演示

const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const entries = urlParams.entries(); //returns an iterator of decoded [key,value] tuples
const params = paramsToObject(entries); //{abc:"foo",def:"[asf]",xyz:"5"}

使用 Object.fromEntries 和 spread

我们可以使用 Object.fromEntries,将 paramsToObject 替换为 Object.fromEntries(entries)

要迭代的值对是以名称和值为键值对的列表。

由于 URLParams 返回一个 可迭代对象,使用 spread operator 而不是调用 .entries 也会按照其规范生成条目:

const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const params = Object.fromEntries(urlParams); // {abc: "foo", def: "[asf]", xyz: "5"}

注意:根据URLSearchParams规范,所有的值都会自动转换为字符串。

多个相同键

正如@siipe所指出的那样,包含多个相同键值的字符串将被强制转换为最后一个可用的值:foo=first_value&foo=second_value实际上会变成:{foo: "second_value"}

根据这个回答:https://dev59.com/aXI-5IYBdhLWcg3wpqIK#1746566,没有规范来决定如何处理它,每个框架的行为可能不同。

常见的用例是将两个相同的值合并为一个数组,使输出对象变为:

{foo: ["first_value", "second_value"]}

这可以通过以下代码实现:

const groupParamsByKey = (params) => [...params.entries()].reduce((acc, tuple) => {
 // getting the key and value from each tuple
 const [key, val] = tuple;
 if(acc.hasOwnProperty(key)) {
    // if the current key is already an array, we'll add the value to it
    if(Array.isArray(acc[key])) {
      acc[key] = [...acc[key], val]
    } else {
      // if it's not an array, but contains a value, we'll convert it into an array
      // and add the current value to it
      acc[key] = [acc[key], val];
    }
 } else {
  // plain assignment if no special case is present
  acc[key] = val;
 }

return acc;
}, {});

const params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5&def=dude');
const output = groupParamsByKey(params) // {abc: "foo", def: ["[asf]", "dude"], xyz: 5}

3
我不推荐使用这个解决方案。URLSearchParams规范有一些不合逻辑的地方(https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#Gotchas)。 - Seph Reed
8
抱歉,但逻辑与此无关。有人可能会认为它是一个“搜索字符串”解析器-这就是它的设计目的,无论它是否与URL相关联。 - silicakes
2
这对我来说看起来不错,但是为了获取重复键的值,我们可以使用以下代码: let temp={};Object.keys(params).map(key=>{temp[key]=urlParams.getAll(key)}) - Tirumaleshwar Keregadde
它会强制每个值都以数组形式返回,这并不好也不坏,但值得考虑。 - silicakes
2
数组键以 foo[]: [1, 2, 3] 的形式出现,但我想要的是 foo: [1, 2, 3],所以我添加了一行额外的代码:const [ _key, val ] = tuple const key = _key.replace(/\[]$/, '') - Emeke Ajeh
显示剩余4条评论

353

一句话概括,干净简洁。

const params = Object.fromEntries(new URLSearchParams(location.search));

针对您的具体情况,应该是:

const str = 'abc=foo&def=%5Basf%5D&xyz=5';
const params = Object.fromEntries(new URLSearchParams(str));
console.log(params);


5
这个可以使用,但需要小心。?someValue=false会变成 { someValue: "false" } - Simon
23
对于重复的键不起作用。如果我们尝试类似 ?foo=bar1&foo=bar2 的操作,我们只会得到 { foo: 'bar2' }。 Node.js 请求对象将其解析为 { foo: ['bar1', 'bar2'] } - Siipe
4
这在处理数组时会出现问题,例如:x=1&x=2 -> 结果 {x:2}。 - Sh eldeeb
8
这是更准确和有用的答案。 - Felix Jr
+1 - 这一行代码可以替换20-30行“旧的JS”代码,实现相同的想法(将参数保存在对象中)。谢谢。 - Ezra Siton
显示剩余2条评论

70

2023一行代码方法

对于一般情况,您想将查询参数解析为对象:

Object.fromEntries(new URLSearchParams(location.search));

对于您的具体情况:

Object.fromEntries(new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5'));

1
也可以使用[...new URLSearchParams(window.location.search)].reduce((o, i) => ({ ...o, [i[0]]: i[1] }), {}); - dman
2
请注意,这会将 ?booleanValue=true 转换为 { booleanValue: "true" },这可能是不希望的。 - Chris Hayes
7
当您的查询参数中有一个数组字段时,这是一个糟糕的解决方案。例如,如果您的查询参数为ids=1&ids=2,则会返回{ ids: '2' }。 - Tal Kohavy

34

将字符串使用 & 进行分割,得到键值对,然后针对每个键值对再使用 = 进行分割。以下是一个示例:

var str = "abc=foo&def=%5Basf%5D&xy%5Bz=5"
var obj = str.split("&").reduce(function(prev, curr, i, arr) {
    var p = curr.split("=");
    prev[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
    return prev;
}, {});

另一种方法是使用正则表达式:

var obj = {}; 
str.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
    obj[decodeURIComponent(key)] = decodeURIComponent(value);
}); 

这是从John Resig的"搜索而非替换"调整而来。

嘿!在左边还应该加上decodeURIComponent(p[0])哦 :) - Alex
第一个示例不能处理空查询字符串。 - Michał Perłakowski

29

目前我找到的解决方案并不涵盖更复杂的情况。

我需要将像以下这样的查询字符串:

https://random.url.com?Target=Offer&Method=findAll&filters%5Bhas_goals_enabled%5D%5BTRUE%5D=1&filters%5Bstatus%5D=active&fields%5B%5D=id&fields%5B%5D=name&fields%5B%5D=default_goal_name

转换为如下对象:

{
    "Target": "Offer",
    "Method": "findAll",
    "fields": [
        "id",
        "name",
        "default_goal_name"
    ],
    "filters": {
        "has_goals_enabled": {
            "TRUE": "1"
        },
        "status": "active"
    }
}

或者:

https://random.url.com?Target=Report&Method=getStats&fields%5B%5D=Offer.name&fields%5B%5D=Advertiser.company&fields%5B%5D=Stat.clicks&fields%5B%5D=Stat.conversions&fields%5B%5D=Stat.cpa&fields%5B%5D=Stat.payout&fields%5B%5D=Stat.date&fields%5B%5D=Stat.offer_id&fields%5B%5D=Affiliate.company&groups%5B%5D=Stat.offer_id&groups%5B%5D=Stat.date&filters%5BStat.affiliate_id%5D%5Bconditional%5D=EQUAL_TO&filters%5BStat.affiliate_id%5D%5Bvalues%5D=1831&limit=9999

转化为:

{
    "Target": "Report",
    "Method": "getStats",
    "fields": [
        "Offer.name",
        "Advertiser.company",
        "Stat.clicks",
        "Stat.conversions",
        "Stat.cpa",
        "Stat.payout",
        "Stat.date",
        "Stat.offer_id",
        "Affiliate.company"
    ],
    "groups": [
        "Stat.offer_id",
        "Stat.date"
    ],
    "limit": "9999",
    "filters": {
        "Stat.affiliate_id": {
            "conditional": "EQUAL_TO",
            "values": "1831"
        }
    }
}

我将多种解决方案编译和整合为一个实际可行的方案:

代码:

var getParamsAsObject = function (query) {

    query = query.substring(query.indexOf('?') + 1);

    var re = /([^&=]+)=?([^&]*)/g;
    var decodeRE = /\+/g;

    var decode = function (str) {
        return decodeURIComponent(str.replace(decodeRE, " "));
    };

    var params = {}, e;
    while (e = re.exec(query)) {
        var k = decode(e[1]), v = decode(e[2]);
        if (k.substring(k.length - 2) === '[]') {
            k = k.substring(0, k.length - 2);
            (params[k] || (params[k] = [])).push(v);
        }
        else params[k] = v;
    }

    var assign = function (obj, keyPath, value) {
        var lastKeyIndex = keyPath.length - 1;
        for (var i = 0; i < lastKeyIndex; ++i) {
            var key = keyPath[i];
            if (!(key in obj))
                obj[key] = {}
            obj = obj[key];
        }
        obj[keyPath[lastKeyIndex]] = value;
    }

    for (var prop in params) {
        var structure = prop.split('[');
        if (structure.length > 1) {
            var levels = [];
            structure.forEach(function (item, i) {
                var key = item.replace(/[?[\]\\ ]/g, '');
                levels.push(key);
            });
            assign(params, levels, params[prop]);
            delete(params[prop]);
        }
    }
    return params;
};

这是最佳答案,因为它确实可以正确处理复杂的查询。 - Georgy Ivanov
1
我认为这只会让事情变得更加复杂,我只会传递 obj=encodeURIComponent(JSON.stringify({what:{ever:','},i:['like']})) - chickens
现在这就是我想说的。这是我在这里找到的最完整的答案! - codemonkey

22
一个简明的解决方案:
location.search
  .slice(1)
  .split('&')
  .map(p => p.split('='))
  .reduce((obj, pair) => {
    const [key, value] = pair.map(decodeURIComponent);
    obj[key] = value;
    return obj;
  }, {});

这在数组中失败,即:x=1&x=2。 - Sh eldeeb
谢谢。?test&withval=1的工作正常。{test: undefined, withval: 1} - parth.hirpara

18

这是简化版,显然你需要添加一些错误检查:

var obj = {};
var pairs = queryString.split('&');
for(i in pairs){
    var split = pairs[i].split('=');
    obj[decodeURIComponent(split[0])] = decodeURIComponent(split[1]);
}

1
你是不是忘了解码字符串,将%5B和%5D转换为字符? - jfriend00
@Alex - 你用的是更新后的代码还是原始代码?原始代码有一个问题和一个打字错误。 - Justin Niessner
1
当参数的值包含“=”时,它无法正确处理参数。它会将值截断到第一个“=”。 - Greck
JSON.parse('{"' + decodeURIComponent(query.replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}')); 对我有效。 - Danil Gaponov
1
它不适用于 name[]=test1&name[]=test2,并且会导致 name[]=test2 的结果。 - Rafee
为了处理带有第二个“=”字符的参数,请将split[1]替换为split.slice(1).join("=") - tigrou

12

对于Node JS,您可以使用Node JS API querystring

const querystring = require('querystring');

querystring.parse('abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar');
// returns the object

文档:https://nodejs.org/api/querystring.html


10

我发现$.String.deparam是最完整的预构建解决方案(可以处理嵌套对象等)。请查看文档


只要您的输入始终是序列化的查询字符串,就不必担心嵌套问题,更轻量级的解决方案可能更好。 - mattacular
当然了...但这已经被完成和测试过了(例如Justin在初始答案中忘记进行URI解码-这些小问题可能会使事情比最初看起来更加复杂)。 - Daff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接