Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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.

share|improve this question
1  
Have you tried extracting only the source of jquery.param? james.padolsey.com/jquery –  Ricardo Castañeda Jan 7 at 15:12
    
@RicardoCastañeda That might just be the best idea. I had hoped there might be a more straightforward solution. The extracted code from jQuery would just be the perfect answer, and I'd accept it right away ;-) (It would also be a great addition to You might not need jQuery). I guess I'll have a look. –  Malte Skoruppa Jan 7 at 15:20
    
I have added an answer with the code, if that is not the correct source you can edit the post. –  Ricardo Castañeda Jan 7 at 15:40

3 Answers 3

up vote 2 down vote accepted

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] && topLevel[keyProp].indexOf(data[key]) !== 0){
                string += keyProp;
            }
            if (topLevel[key]) {
                string += key;
            } else {
                string += '[' + key + ']';
            }
            if (data[key] instanceof Array) {
                string += stringifyParam(data[key], topLevel, key);
            } else if(data[key] instanceof Object){
                string += stringifyParam(data[key], topLevel);            
            } else {
                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
share|improve this answer
    
Thanks Simon, but this doesn't appear to do exactly the right thing. If you append console.log($.param(data)); at the end of your fiddle (and require jQuery), you will see the output of $.param is different. I tried your code, but unfortunately my server chokes if I send it the output from your function. –  Malte Skoruppa Jan 7 at 15:17
1  
Check my updated code for a working example –  Simon Staton Jan 7 at 16:24
    
love your approach! Very nice code. I just got lazy and fudged my initial answer into what I thought was wanted. :P –  Todd Jan 7 at 21:23
    
I ended up extracting the jQuery source code, but thanks for this. :) @Todd Sorry if it wasn't entirely clear what I wanted, I thought that "do what $.param does" was precise enough, and I tried to give some context, but maybe it ended up being confusing. –  Malte Skoruppa Jan 8 at 10:08

You could go about doing recursive code but why not try the simple scheme JSON, which was why it was created - To exchange data between client and server in an easier manner.

Just do this

request.send(JSON.stringify(data));

JSON.stringify accepts an Object which will then be converted to a valid JSON, which can be parsed in the server-side.

To know more about the JSON, there can't be a better way than going through it's tag excerpt here

share|improve this answer
    
Thanks for the answer. I know about JSON, however unfortunately I am free to modify the client-side code, but I can't modify the server-side code. The server expects a query string instead of a JSON string, so I need a function which converts an objects to a query string, like $.param does. –  Malte Skoruppa Jan 7 at 15:10

you could use encode and decode URIComponent functions to accomplish this.

edit

what about this:

var qs = Object.keys(obj).reduce(function(a,k){
    a.push(k+'='+encodeURIComponent(JSON.stringify(obj[k])));
    return a;
},[]).join('&');

// "apple=%5B%7B%22kiwi%22%3A%22orange%22%7D%2C%7B%22banana%22%3A%22lemon%22%7D%5D&pear=%22passion%20fruit%22"

instead of this:

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

var data = encodeURIComponent(JSON.stringify(obj));

// "%7B%22apple%22%3A%5B%7B%22kiwi%22%3A%22orange%22%7D%2C%7B%22banana%22%3A%22lemon%22%7D%5D%2C%22pear%22%3A%22passion%20fruit%22%7D"

var obj2 = JSON.parse(decodeURIComponent(data));

// {"apple":[{"kiwi":"orange"},{"banana":"lemon"}],"pear":"passion fruit"}
share|improve this answer
    
Concerning your edit: it's still not the same as $.param(data). As stated above, the expected output would be apple%5B0%5D%5Bkiwi%5D=orange&apple%5B1%5D%5Bbanana%5D=lemon&pear=passion+fruit‌​ (which is apple[0][kiwi]=orange&apple[1][banana]=lemon&pear=passion fruit in URL-decoded form). Your function more or less URL-encodes a JSON string, which is different. –  Malte Skoruppa Jan 7 at 16:00
    
lol. I'm trying, man. You're working with JSON, dawg. –  Todd Jan 7 at 16:08

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.