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

问题描述

我目前正在尝试将 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: ''
   }
}

我尝试使用名为 query-string 的库,但结果如下:

{
  '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]': '' 
}

我尝试使用我发现的两个实现here

  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(/\](.+?)(,$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

script    | Traceback (most recent call last):
script    |   File "/test_db/testDb.py",line 10,in <module>
script    |     TestDb().run()
script    |   File "/test_db/testDb.py",line 5,in run
script    |     conn = pymysql.connect(host='mysql',port=3306,database='sta_db',user='root',password='Alphashadow1381')
script    |   File "/usr/local/lib/python3.9/site-packages/pymysql/__init__.py",line 94,in Connect
script    |     return Connection(*args,**kwargs)
script    |   File "/usr/local/lib/python3.9/site-packages/pymysql/connections.py",line 327,in __init__
script    |     self.connect()
script    |   File "/usr/local/lib/python3.9/site-packages/pymysql/connections.py",line 619,in connect
script    |     raise exc
script    | pymysql.err.OperationalError: (2003,"Can't connect to MySQL server on 'mysql' ([Errno 111] Connection refused)")

对于旧版浏览器

这是 EcmaScript 2015 兼容的:

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