Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encode object literal as URL query string in Javascript

I am trying to get rid of the jQuery dependency in a project. One thing that project does is posting data to a server like so:

var data = {"apple": [{"kiwi": "orange"}, {"banana": "lemon"}], "pear": "passion fruit"};
$.post( url, data);

Thanks to You might not need jQuery, I know how to rewrite $.post in pure Javascript using XMLHttpRequest:

var request = new XMLHttpRequest();
request.open( 'POST', url, true);
request.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.send( data);

Unfortunately, this description seems to assume that the data object is already a URL-encoded query string, which is clearly not the case in the above example. It turns out jQuery does more than that: With the given data object, the above $.post call would first convert it to a query string, which would look like so:

apple%5B0%5D%5Bkiwi%5D=orange&apple%5B1%5D%5Bbanana%5D=lemon&pear=passion+fruit

The code snippet using XMLHttpRequest does not do so, and, thus, the server will throw errors at me.

jQuery also has a wonderful method call $.param which does exactly this conversion. The above code snippet using XMLHttpRequest will work marvelously if in the last line I do

request.send( $.param(data));

But then, I did not get rid of the jQuery dependency. So I'm looking for a pure Javascript equivalent of $.param. Does anyone have something like that?

Note: The question Plain Javascript Equivalent of jQuery.param() asks a similar question, but the accepted answer only works in very simple cases. Applying the function given in that answer to my above data object yields:

apple=%5Bobject%20Object%5D%2C%5Bobject%20Object%5D&pear=passion%20fruit

...which is obviously different from the result of $.param(data) given above, and loses information since it doesn't work recursively.

like image 841
Malte Skoruppa Avatar asked Jul 16 '26 02:07

Malte Skoruppa


1 Answers

I have made a quick function for you which should achieve this for you, it will create parameters from your key=>value pairs and stringify your non primitive values.

var objToParams = function(obj){
    var paramString = '';
    for (var key in data) {
        var value = obj[key];
        if(obj[key] instanceof Array || obj[key] instanceof Object){
            value = encodeURIComponent(JSON.stringify(value));
        }
        if (paramString != "") paramString += "&";
        paramString += key + "=" + encodeURIComponent(value);
    }
    return paramString;
}

var data = {"apple": [{"kiwi": "orange"}, {"banana": "lemon"}], "pear": "passion fruit"};
console.log(objToParams(data));

http://jsfiddle.net/7buy3rjy/

Edit, from your comment this should work and is now matching the output of $.param:

http://jsfiddle.net/mg511z7w/

var data = {"apple": [{"kiwi": "orange"}, {"banana": "lemon"}], "pear": "passion fruit"};

var stringifyParam = function(data, topLevel, keyProp) {
        var string = '';
        for (var key in data) {
            if(keyProp && topLevel[keyProp] ) {
                if ( (topLevel[keyProp] instanceof Array&&topLevel[keyProp].indexOf(data[key])!==0) ) {
                    string += keyProp;
                } else if ( (topLevel[keyProp] instanceof Object&&topLevel[keyProp][key]) ) {
                    string += keyProp;
                }
            }
            if (typeof(topLevel[key])=='undefined') {
                string += '[' + key + ']';
            }
            if (data[key] instanceof Array) {
                string += stringifyParam(data[key], topLevel, key);
            } else if(data[key] instanceof Object){
                string += stringifyParam(data[key], topLevel, key);            
            } else {
                if (typeof(topLevel[key])!='undefined') {
                    string += key;
                }
                string += '=' + data[key];
                string += '&';
            }
        }
        return string;
    },
    toParam = function(data){
        var string = stringifyParam(data,data);
        return encodeURI(string.substring(0,string.length-1).split(' ').join('+'));
    };

console.log(toParam(data)); //apple%5B0%5D%5Bkiwi%5D=orange&apple%5B1%5D%5Bbanana%5D=lemon&pear=passion+fruit
console.log($.param(data)); //apple%5B0%5D%5Bkiwi%5D=orange&apple%5B1%5D%5Bbanana%5D=lemon&pear=passion+fruit
like image 175
Simon Staton Avatar answered Jul 17 '26 17:07

Simon Staton



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!