如何将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个回答

2

console.log(decodeURI('abc=foo&def=%5Basf%5D&xyz=5')
  .split('&')
  .reduce((result, current) => {
    const [key, value] = current.split('=');

    result[key] = value;

    return result
  }, {}))


2
有一个轻量级的库叫做YouAreI.js,已经经过测试,并且使这项工作非常容易。
YouAreI = require('YouAreI')
uri = new YouAreI('http://user:pass@www.example.com:3000/a/b/c?d=dad&e=1&f=12.3#fragment');

uri.query_get() => { d: 'dad', e: '1', f: '12.3' }

2

1
如果您需要递归,可以使用微小的 js-extension-ling 库。
npm i js-extension-ling

const jsx = require("js-extension-ling");

console.log(jsx.queryStringToObject("a=1")); 
console.log(jsx.queryStringToObject("a=1&a=3")); 
console.log(jsx.queryStringToObject("a[]=1")); 
console.log(jsx.queryStringToObject("a[]=1&a[]=pomme")); 
console.log(jsx.queryStringToObject("a[0]=one&a[1]=five"));
console.log(jsx.queryStringToObject("http://blabla?foo=bar&number=1234")); 
console.log(jsx.queryStringToObject("a[fruits][red][]=strawberry"));
console.log(jsx.queryStringToObject("a[fruits][red][]=strawberry&a[1]=five&a[fruits][red][]=cherry&a[fruits][yellow][]=lemon&a[fruits][yellow][688]=banana"));

这将输出类似于以下内容:
{ a: '1' }
{ a: '3' }
{ a: { '0': '1' } }
{ a: { '0': '1', '1': 'pomme' } }
{ a: { '0': 'one', '1': 'five' } }
{ foo: 'bar', number: '1234' }
{
  a: { fruits: { red: { '0': 'strawberry' } } }
}
{
  a: {
    '1': 'five',
    fruits: {
      red: { '0': 'strawberry', '1': 'cherry' },
      yellow: { '0': 'lemon', '688': 'banana' }
    }
  }
}


注意:它基于 locutus 的 parse_str 函数 (https://locutus.io/php/strings/parse_str/)。

1
这是我使用的一个例子:
var params = {};
window.location.search.substring(1).split('&').forEach(function(pair) {
  pair = pair.split('=');
  if (pair[1] !== undefined) {
    var key = decodeURIComponent(pair[0]),
        val = decodeURIComponent(pair[1]),
        val = val ? val.replace(/\++/g,' ').trim() : '';

    if (key.length === 0) {
      return;
    }
    if (params[key] === undefined) {
      params[key] = val;
    }
    else {
      if ("function" !== typeof params[key].push) {
        params[key] = [params[key]];
      }
      params[key].push(val);
    }
  }
});
console.log(params);

基本用法,例如:
?a=aa&b=bb
Object {a: "aa", b: "bb"}

重复参数,例如:
?a=aa&b=bb&c=cc&c=potato
Object {a: "aa", b: "bb", c: ["cc","potato"]}

缺失键名,例如:
?a=aa&b=bb&=cc
Object {a: "aa", b: "bb"}

缺失键值,例如:
?a=aa&b=bb&c
Object {a: "aa", b: "bb"}

上述 JSON/正则表达式解决方案在以下奇怪的 URL 上会抛出语法错误:
?a=aa&b=bb&c=&=dd&e
Object {a: "aa", b: "bb", c: ""}


1
//under ES6 
const getUrlParamAsObject = (url = window.location.href) => {
    let searchParams = url.split('?')[1];
    const result = {};
    //in case the queryString is empty
    if (searchParams!==undefined) {
        const paramParts = searchParams.split('&');
        for(let part of paramParts) {
            let paramValuePair = part.split('=');
            //exclude the case when the param has no value
            if(paramValuePair.length===2) {
                result[paramValuePair[0]] = decodeURIComponent(paramValuePair[1]);
            }
        }

    }
    return result;
}

与其他基于正则表达式的答案相比,我真的很喜欢这种方法(在2017年)。如果polyfill箭头函数(或重写为传统函数),我认为这应该可以在各个浏览器中很好地工作。 - Scribblemacher
借助 Babel 的帮助,@Scribblemacher 可以在其他环境下很好地完成它。 - XYz Amos

1
这似乎是最佳解决方案,因为它考虑了相同名称的多个参数。
    function paramsToJSON(str) {
        var pairs = str.split('&');
        var result = {};
        pairs.forEach(function(pair) {
            pair = pair.split('=');
            var name = pair[0]
            var value = pair[1]
            if( name.length )
                if (result[name] !== undefined) {
                    if (!result[name].push) {
                        result[name] = [result[name]];
                    }
                    result[name].push(value || '');
                } else {
                    result[name] = value || '';
                }
        });
        return( result );
    }

<a href="index.html?x=1&x=2&x=3&y=blah">something</a>
paramsToJSON("x=1&x=2&x=3&y=blah"); 

console yields => {x: Array[3], y: "blah"} where x is an array as is proper JSON

后来我决定将其转换为jQuery插件...

$.fn.serializeURLParams = function() {
    var result = {};

    if( !this.is("a") || this.attr("href").indexOf("?") == -1 ) 
        return( result );

    var pairs = this.attr("href").split("?")[1].split('&');
    pairs.forEach(function(pair) {
        pair = pair.split('=');
        var name = decodeURI(pair[0])
        var value = decodeURI(pair[1])
        if( name.length )
            if (result[name] !== undefined) {
                if (!result[name].push) {
                    result[name] = [result[name]];
                }
                result[name].push(value || '');
            } else {
                result[name] = value || '';
            }
    });
    return( result )
}

<a href="index.html?x=1&x=2&x=3&y=blah">something</a>
$("a").serializeURLParams(); 

console yields => {x: Array[3], y: "blah"} where x is an array as is proper JSON

现在,第一个只接受参数,但是jQuery插件将获取整个URL并返回序列化的参数。

1
这是我快速的版本,基本上是将由'&'分隔的URL参数拆分为数组元素,然后迭代该数组并将由'='分隔的键/值对添加到对象中。 我使用decodeURIComponent()将编码字符转换为其正常字符串等效项(因此%20变成了空格,%26变成了'&'等):
function deparam(paramStr) {
    let paramArr = paramStr.split('&');     
    let paramObj = {};
    paramArr.forEach(e=>{
        let param = e.split('=');
        paramObj[param[0]] = decodeURIComponent(param[1]);
    });
    return paramObj;
}

例子:

deparam('abc=foo&def=%5Basf%5D&xyz=5')

返回
{
    abc: "foo"
    def:"[asf]"
    xyz :"5"
}

唯一的问题是xyz是一个字符串而不是数字(由于使用decodeURIComponent()),但除此之外,这并不是一个坏的起点。

0

这里是silicakes' approach的更简化版本。

以下函数可以从USVStringLocation中解析查询字符串。

/**
 * Returns a plain object representation of a URLSearchParams object.
 * @param {USVString} search - A URL querystring
 * @return {Object} a key-value pair object from a URL querystring
 */
const parseSearch = (search) =>
  [...new URLSearchParams(search).entries()]
    .reduce((acc, [key, val]) => ({
      ...acc,
      // eslint-disable-next-line no-nested-ternary
      [key]: Object.prototype.hasOwnProperty.call(acc, key)
        ? Array.isArray(acc[key])
          ? [...acc[key], val]
          : [acc[key], val]
        : val
    }), {});

/**
 * Returns a plain object representation of a URLSearchParams object.
 * @param {Location} location - Either a document or window location, or React useLocation()
 * @return {Object} a key-value pair object from a URL querystring
 */
const parseLocationSearch = (location) => parseSearch(location.search);

console.log(parseSearch('?foo=bar&x=y&ids=%5B1%2C2%2C3%5D&ids=%5B4%2C5%2C6%5D'));
.as-console-wrapper { top: 0; max-height: 100% !important; }

这是上面代码的一行代码(125字节):

其中fparseSearch

f=s=>[...new URLSearchParams(s).entries()].reduce((a,[k,v])=>({...a,[k]:a[k]?Array.isArray(a[k])?[...a[k],v]:[a[k],v]:v}),{})

编辑

这里介绍一种序列化和更新的方法:

const parseSearch = (search) =>
  [...new URLSearchParams(search).entries()]
    .reduce((acc, [key, val]) => ({
      ...acc,
      // eslint-disable-next-line no-nested-ternary
      [key]: Object.prototype.hasOwnProperty.call(acc, key)
        ? Array.isArray(acc[key])
          ? [...acc[key], val]
          : [acc[key], val]
        : val
    }), {});

const toQueryString = (params) =>
  `?${Object.entries(params)
    .flatMap(([key, values]) =>
      Array.isArray(values)
        ? values.map(value => [key, value])
        : [[key, values]])
    .map(pair => pair.map(val => encodeURIComponent(val)).join('='))
    .join('&')}`;

const updateQueryString = (search, update) =>
  (parsed =>
    toQueryString(update instanceof Function
      ? update(parsed)
      : { ...parsed, ...update }))
  (parseSearch(search));

const queryString = '?foo=bar&x=y&ids=%5B1%2C2%2C3%5D&ids=%5B4%2C5%2C6%5D';
const parsedQuery = parseSearch(queryString);
console.log(parsedQuery);
console.log(toQueryString(parsedQuery) === queryString);

const updatedQuerySimple = updateQueryString(queryString, {
  foo: 'baz',
  x: 'z',
});
console.log(updatedQuerySimple);
console.log(parseSearch(updatedQuerySimple));

const updatedQuery = updateQueryString(updatedQuerySimple, parsed => ({
  ...parsed,
  ids: [
    ...parsed.ids,
    JSON.stringify([7,8,9])
  ]
}));
console.log(updatedQuery);
console.log(parseSearch(updatedQuery));
.as-console-wrapper { top: 0; max-height: 100% !important; }


0

我需要处理URL的查询部分中的+号(decodeURIComponent不支持),因此我改编了Wolfgang的代码如下:

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

在我的情况下,我使用jQuery获取URL-ready表单参数,然后使用这个技巧将其构建成一个对象,然后我可以轻松地更新对象上的参数并重新构建查询URL,例如:
var objForm = JSON.parse('{"' + $myForm.serialize().replace(/\+/g, ' ').replace(/&/g, '","').replace(/=/g,'":"') + '"}',
             function(key, value) { return key===""?value:decodeURIComponent(value)});
objForm.anyParam += stringToAddToTheParam;
var serializedForm = $.param(objForm);

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