简体   繁体   中英

How to convert an x-www-form-urlencoded string to JSON?

Exampple of application/x-www-form-urlencoded string

CorrelationId=1&PickedNumbers%5B%5D=1&PickedNumbers%5B%5D=2&PickedNumbers%5B%5D=3&PickedNumbers%5B%5D=4

Into JSON

var gamePlayData = {
            CorrelationId: gameId,
            PickedNumbers: ["1","2","3","4"]
        };

This is a core module of Node.js now: https://nodejs.org/api/querystring.html#querystring_querystring_parse_str_sep_eq_options

var qs = require('querystring')

var json = qs.parse('why=not&sad=salad')
    // { why: 'not', sad: 'salad' }

Works with encoded characters too:

var json2 = qs.parse('http%3A%2F%2Fexample.com&sad=salad')
    // { url: 'http://example.com', sad: 'salad' }

I've been dealing with this recently: I had to parse data that could contain objects nested up to 5 levels deep. I needed the code to be able to deal with both rather complex data, but not fail to decode a URI as simple as id=213 .

I spent quite some time on google, trying to find a (semi-)elegant solution to this problem, and this question kept showing up. Since it gets 1 view/day (give or take) I've decided to post my solution here, hope it helps someone out:

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

I've tested it, with data like the string below (4 levels deep):

str  = "id=007&name[first]=james&name[last]=bond&name[title]=agent&personalia[occupation]=spy&personalia[strength]=women&personalia[weakness]=women&tools[weapons][close][silent]=garrot&tools[weapons][medium][silent]=pistol_supressed&tools[weapons][medium][loud]=smg&tools[weapons][far][silent]=sniper&tools[movement][slow]=foot&tools[movement][far]=DBS";

Which neatly returns an object, that, when passed through JSON.stringify comes out like this:

{"id":"007","name":{"title":"agent","first":"james","last":"bond"},"personalia":{"weakness":"women","occupation":"spy","strength":"women"},"tools":{"movement":{"far":"DBS","slow":"foot"},"weapons":{"close":{"silent":"garrot"},"medium":{"silent":"pistol_supressed","loud":"smg"},"far":{"silent":"sniper"}}}}

It passes a JSlint check, when ignoring white space, . and [^...] and accepting ++ . All in all, I'd consider that to be acceptable.

You can use qs if you're using node, or browserify.

var qs = require('qs')
var encodedString = "CorrelationId=1&PickedNumbers%5B%5D=1&PickedNumbers%5B%5D=2&PickedNumbers%5B%5D=3&PickedNumbers%5B%5D=4" 

console.log(qs.parse(encodedString))
// { CorrelationId: '1', PickedNumbers: [ '1', '2', '3', '4' ] }

the following code should do the trick:

var str = 'CorrelationId=1&PickedNumbers%5B%5D=1&PickedNumbers%5B%5D=2&PickedNumbers%5B%5D=3&PickedNumbers%5B%5D=4';
var keyValuePairs = str.split('&');
var json = {};
for(var i=0,len = keyValuePairs.length,tmp,key,value;i <len;i++) {
    tmp = keyValuePairs[i].split('=');
    key = decodeURIComponent(tmp[0]);
    value = decodeURIComponent(tmp[1]);
    if(key.search(/\[\]$/) != -1) {
        tmp = key.replace(/\[\]$/,'');
        json[tmp] = json[tmp] || [];
        json[tmp].push(value);
    }
    else {
        json[key] = value;
    }
}

Here's a pure-JavaScript way to do it. JavaScript frameworks might also help you out with this. EDIT: Just for kicks, I threw in dictionary parsing, too. See the 2nd example.

function decodeFormParams(params) {
  var pairs = params.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;
}

decodeFormParams("CorrelationId=1&PickedNumbers%5B%5D=1&PickedNumbers%5B%5D=2&PickedNumbers%5B%5D=3&PickedNumbers%5B%5D=4");
// => {"CorrelationId":"1","PickedNumbers":["1","2","3","4"]}

decodeFormParams("a%5Bb%5D=c&a%5Bd%5D=e");
// => {"a":{"b":"c","d":"e"}}

Updated answer for 2022, works both in the browser and in node. Use URLSearchParams class.

Note: The param name PickedNumbers%5B%5D will turn to be the literal string PickedNumbers[] . You don't need to encode the brackets in order to make it an array

    const paramsStr = 'CorrelationId=1&PickedNumbers%5B%5D=1&PickedNumbers%5B%5D=2&PickedNumbers%5B%5D=3&PickedNumbers%5B%5D=4';
    const params = new URLSearchParams(paramsStr);
    //access a specific param
    console.log(params.get('PickedNumbers[]')); // '4'
    console.log(params.getAll('PickedNumbers[]')); // ['1','2','3','4']
     const o = Object.fromEntries(Array.from(params.keys()).map(k => [k, params.getAll(k).length===1 ? params.get(k) : params.getAll(k)]));
    console.log(JSON.stringify(o)); //full object

Try this->

// convert string to object
str = 'a=6&id=99';
var arr = str.split('&');
var obj = {};
for(var i = 0; i < arr.length; i++) {
    var bits = arr[i].split('=');
    obj[bits[0]] = bits[1];
}
//alert(obj.a);
//alert(obj.id);

// convert object back to string
str = '';
for(key in obj) {
    str += key + '=' + obj[key] + '&';
}
str = str.slice(0, str.length - 1); 
alert(str);

Or use this (JQuery) http://api.jquery.com/jQuery.param/

You need the opposite of jQuery.param. One of the options is http://benalman.com/code/projects/jquery-bbq/examples/deparam/

A one-liner:

let s = 'a=1&b=2&c=3';

Object.fromEntries(
  s.split('&')
   .map(s => s.split('='))
   .map(pair => pair.map(decodeURIComponent)))

// -> {a: "1", b: "2", c: "3"}

and if you want repeated parameters to be represented as arrays:

let s = 'a=1&b=2&c[]=3&c[]=4&c[]=5&c[]=6';

s
  .split('&')
  .map(s => s.split('='))
  .map(pair => pair.map(decodeURIComponent))
  .reduce((memo, [key, value]) => { 
    if (!(key in memo)) { memo[key] = value; } 
    else { 
      if (!(memo[key] instanceof Array)) 
        memo[key] = [memo[key], value]; 
        else 
        memo[key].push(value); 
      } 
    return memo; 
}, {})

// -> {"a":"1","b":"2","c[]":["3","4","5","6"]}
var jsonMessage = "{\"".concat(message.replace("&", "\",\"").replace("=", "\":\"")).concat("\"}");

In typescript, works for me:
Use qs.parse to transform in object ParsedQs.
Use as unknow to implicit type unknow and before force convert to string.
Use JSON.parse to convert an string to object.
It was useful to use validations with Joi .

const payload = JSON.parse(JSON.stringify(qs.parse(request.body) as unknown as string));

Payload (cURL):

--data-urlencode 'notification=123-456123' \
--data-urlencode 'test=123456' \
--data-urlencode 'ajieoajeoa=Lorem ipsum'

Result:

{
  notification: '123-456123',
  test: '123456',
  ajieoajeoa: 'Lorem ipsum'
}
public static void Main()

{

string str ="RESULT=0&PNREF=A10AABBF8DF2&RESPMSG=Approved&AUTHCODE=668PNI&PREFPSMSG=No Rules Triggered&POSTFPSMSG=No Rules Triggered";

    var sr = str.Replace("&", "=");

    string[] sp = sr.Split('=');

    var spl = sp.Length;

    int n = 1;

    var ss = "{";

    for (var k = 0; k < spl; k++)
    {
        if (n % 2 == 0)
        {
            if (n == spl)
            {
                ss += '"' + sp[k] + '"';
            }
            else
            {
                ss += '"' + sp[k] + '"' + ",";
            }
        }
        else
        {
            ss += '"' + sp[k] + '"' + ":";
        }
        n++;
    }
    ss += "}";
    Console.WriteLine(ss);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM