如何将查询字符串转换为多级对象



我目前正在尝试将URL查询字符串转换为JavaScript对象,但进展并不顺利。我已经在Stack Overflow周围寻找可能的解决方案,但还没有找到我想要的东西。这是查询字符串:

"class%5Blocation_id%5D=122&student%5Bgender%5D=&student%5Bpicture%5D=&class%5Bquestions%5D%5B2775%5D%5Banswers%5D%5B%5D=Black+canary&ids%5B%5D=32&class%5Bquestions%5D%5B2775%5D%5Banswer%5D=&class%5Bquestions%5D%5B2776%5D%5Banswers%5D%5B%5D=Blue+Whistle&class%5Bquestions%5D%5B2776%5D%5Banswer%5D=&class%5Bdescription%5D="

我正在寻找这样的东西:

{
class: {
description: '',
location_id: '122',
questions: {
2275: {
answer: '',
answers: ['Black canary']
},
2276: {
answer: '',
answers: ['Blue Whistle']
}
}
},
ids: ['32']
student: {
gender: '',
picture: ''
}
}

我尝试使用一个名为查询字符串的库,但这就是我得到的:

{
'class[description]': '',
'class[location_id]': '122',
'class[questions][2775][answer]': '',
'class[questions][2775][answers][]': 'Black canary',
'class[questions][2776][answer]': '',
'class[questions][2776][answers][]': 'Blue Whistle',
'ids[]': '32',
'student[gender]': '',
'student[picture]': '' 
}

我尝试使用我在这里找到的两个实现:

function form2Json(str) {
"use strict";
var obj, i, pt, keys, j, ev;
if (typeof form2Json.br !== 'function') {
form2Json.br = function (repl) {
if (repl.indexOf(']') !== -1) {
return repl.replace(/](.+?)(,|$)/g, function ($1, $2, $3) {
return form2Json.br($2 + '}' + $3);
});
}
return repl;
};
}
str = '{"' + (str.indexOf('%') !== -1 ? decodeURI(str) : str) + '"}';
obj = str.replace(/=/g, '":"').replace(/&/g, '","').replace(/[/g, '":{"');
obj = JSON.parse(obj.replace(/](.+?)(,|$)/g, function ($1, $2, $3) { return form2Json.br($2 + '}' + $3); }));
pt = ('&' + str).replace(/([|]|=)/g, '"$1"').replace(/]"+/g, ']').replace(/&([^[=]+?)([|=)/g, '"&["$1]$2');
pt = (pt + '"').replace(/^"&/, '').split('&');
for (i = 0; i < pt.length; i++) {
ev = obj;
keys = pt[i].match(/(?!:(["))([^"]+?)(?=("]))/g);
for (j = 0; j < keys.length; j++) {
if (!ev.hasOwnProperty(keys[j])) {
if (keys.length > (j + 1)) {
ev[keys[j]] = {};
}
else {
ev[keys[j]] = pt[i].split('=')[1].replace(/"/g, '');
break;
}
}
ev = ev[keys[j]];
}
}
return obj;
}

但最终得到这个:

{
class: {
description: "",
location_id: "122"
},
questions:{
2775: {answers: "Black+canary", answer: ""}
2776: {answers: "Blue+Whistle", answer: ""}
},
ids: {
"": "32"
},
student: {
gender: ""
picture: ""
}
}

ids变成一个对象而不是一个数组,answers变成一个字符串。 我能得到的最接近的是使用这个:

function form2Json(str) {
"use strict";
var pairs = str.split('&'),
result = {};
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('='),
key = decodeURIComponent(pair[0]),
value = decodeURIComponent(pair[1]),
isArray = /[]$/.test(key),
dictMatch = key.match(/^(.+)[([^]]+)]$/);
if (dictMatch) {
key = dictMatch[1];
var subkey = dictMatch[2];
result[key] = result[key] || {};
result[key][subkey] = value;
} else if (isArray) {
key = key.substring(0, key.length - 2);
result[key] = result[key] || [];
result[key].push(value);
} else {
result[key] = value;
}
}
return result;
}

有了这个结果:

{
class: {location_id: "122", description: ""},
class[questions][2775]: {answer: ""},
class[questions][2775][answers]: ["Black+canary"],
class[questions][2776]: {answer: ""},
class[questions][2776][answers]: ["Blue+Whistle"],
ids: ["32"],
student: {gender: "", picture: ""}
}

。最多只有一级关联,并且ids数组被正确分配为数组。我怎样才能做到这一点?

您可以使用URLSearchParams

function form2Object(str) {
let result = {};
for (let [path, value] of new URLSearchParams(str).entries()) {
let keys = path.match(/[^[]]+/g);
let prop = keys.pop();
let acc = result;
for (let key of keys) acc = (acc[key] = acc[key] || {});
acc[prop] = path.endsWith("[]") 
? (acc[prop] || []).concat(value)
: value;
}
return result;
}
// Demo
let str = "class%5Blocation_id%5D=122&student%5Bgender%5D=&student%5Bpicture%5D=&class%5Bquestions%5D%5B2775%5D%5Banswers%5D%5B%5D=Black+canary&ids%5B%5D=32&class%5Bquestions%5D%5B2775%5D%5Banswer%5D=&class%5Bquestions%5D%5B2776%5D%5Banswers%5D%5B%5D=Blue+Whistle&class%5Bquestions%5D%5B2776%5D%5Banswer%5D=&class%5Bdescription%5D=";
console.log(form2Object(str));

对于较旧的浏览器

这是与 EcmaScript 2015 兼容的:

function form2Object(str) {
var result = {};
str.split("&").map(function(eq) {
return eq.split("=").map(decodeURIComponent);
}).forEach(function (entry) {
var path = entry[0];
var value = entry[1];
var keys = path.match(/[^[]]+/g);
var prop = keys.pop();
var acc = keys.reduce(function (acc, key) {
return (acc[key] = acc[key] || {});
}, result);
acc[prop] = path.slice(-2) === "[]"
? (acc[prop] || []).concat(value)
: value;
});
return result;
}
// Demo
let str = "class%5Blocation_id%5D=122&student%5Bgender%5D=&student%5Bpicture%5D=&class%5Bquestions%5D%5B2775%5D%5Banswers%5D%5B%5D=Black+canary&ids%5B%5D=32&class%5Bquestions%5D%5B2775%5D%5Banswer%5D=&class%5Bquestions%5D%5B2776%5D%5Banswers%5D%5B%5D=Blue+Whistle&class%5Bquestions%5D%5B2776%5D%5Banswer%5D=&class%5Bdescription%5D=";
console.log(form2Object(str));

最新更新