Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
236 views
in Technique[技术] by (71.8m points)

javascript - How to convert query string to multi level object

I'm currently trying to convert a URL query string to a JavaScript Object, which isn't going so well. I've looked around Stack Overflow for possible solutions but haven't have quite found what I was looking for. This is the query string:

"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="

I'm looking for something like this:

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

I tried using a library called query-string, but this is what I get:

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

And I tried using two implementations I found 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(/](.+?)(,|$)/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;
  }

But ended up with this:

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

ids becoming an object instead of an array and answers becoming a string. The closest I could get to it was using this:

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

With this 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: ""}
}

...with only up to first level association, and the array of ids being assigned properly as an array. How can I make this work?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can use 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));

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...