3

For this example, assume that I have a list of months in a form, each with a checkbox next to them. I'm looking for help on doing either of two things:

  1. Convert a query string (e.g. "January=on&March=on&September=on") or
  2. Convert an object map: [{ January: 'on' },{ March: 'on' },{ September: 'on' }]

to a single JSON object: { January: 'on', March: 'on', September: 'on' }

I realize that the first map is already JSON, but instead of the object array I'd like it to be a single JSON object. I can build the map with $('form').serializeArray(); and I can build the query string with $('form').serialize();.

The implementation of .serialize() in the jQuery API is simply:

serialize: function() {
    return jQuery.param(this.serializeArray());
},

which is why I could handle either the first or second answer.

The reason I'd like to do this is because I'm switching from PrototypeJS to jQuery, and in PrototypeJS this was as simple as:

Object.toJSON(Form.serializeElements($('myform'), true));

So, does anybody know of a JSON plug-in (I'd like to stick with ONLY jQuery) that could do this easily, or know of a simple method to achieve the result I'm looking for? Thanks!

womp
  • 115,835
  • 26
  • 236
  • 269
Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194

5 Answers5

10

You can try this

String.prototype.QueryStringToJSON = function () {
href = this;
qStr = href.replace(/(.*?\?)/, '');
qArr = qStr.split('&');
stack = {};
for (var i in qArr) {
    var a = qArr[i].split('=');
    var name = a[0],
        value = isNaN(a[1]) ? a[1] : parseFloat(a[1]);
    if (name.match(/(.*?)\[(.*?)]/)) {
        name = RegExp.$1;
        name2 = RegExp.$2;
        //alert(RegExp.$2)
        if (name2) {
            if (!(name in stack)) {
                stack[name] = {};
            }
            stack[name][name2] = value;
        } else {
            if (!(name in stack)) {
                stack[name] = [];
            }
            stack[name].push(value);
        }
    } else {
        stack[name] = value;
    }
}
return stack;
}

Query String

    href="j.html?name=nayan&age=29&salary=20000&interest[]=php&interest[]=jquery&interest[1]=python&interest[2]=Csharp&fan[friend]=rangan&fan[family]=sujan&sports[1]=cricket&sports[2]=football";

usage

alert(href.QueryStringToJSON().toSource())

output

({name:"nayan", age:29, salary:20000, interest:["php", "python", "Csharp"], fan:{friend:"rangan", family:"sujan"}, sports:{1:"cricket", 2:"football"}})
Rahen Rangan
  • 715
  • 5
  • 8
4

kgiannakakis's suggestion to traverse the map was a good starting point, though I don't feel that it qualifies as an answer to my original question. After a couple of hours of head banging, I settled for this, which allows you to serialize elements based on a custom attribute (I didn't want to settle for having to use the 'name' attribute on my form elements, which jQuery requires). I have also started using the JSON library from json.org in order to stringify the object I create. The serializeToJSON function of my plugin is what I was looking for as an answer to my question, the rest is just exta.

Note: This is for a client, so the 'CustomXXX' names and attributes were substituted in for what they actually are

jQuery.fn.extend({
    serializeCustomPropertyArray: function() {
        return this.map(function() {
            return this.elements ? jQuery.makeArray(this.elements) : this;
        }).filter(function() {
            return jQuery(this).attr('CustomAttribute') &&
                (this.checked || /select|textarea/i.test(this.nodeName) ||
                        /text|hidden|password|search/i.test(this.type));
        }).map(function(i, elem) {
            var val = jQuery(this).val();
            return val == null ? null : jQuery.isArray(val) ?
                jQuery.map(val, function(val, i) {
                    return { name: jQuery(elem).attr('CustomAttribute'), value: val };
                }) : { name: jQuery(elem).attr('CustomAttribute'), value: val };
        }).get();
    },
    serializeToJSON: function() {
        var objectMap = this.serializeCustomPropertyArray();
        var objectJson = new Object;
        jQuery.each(objectMap, function() {
            objectJson[this.name] = (this.value !== null) ? this.value : 'null';
        });
        return JSON.stringify(objectJson);
    }
});

This can be called like:

$('#fields').find(':input[CustomGroup="Months"]').serializeToJSON();

Assuming your document looks something like:

<div id="fields">
   <input type="checkbox" CustomGroup="Months" CustomAttribute="January" />January<br />
   <input type="checkbox" CustomGroup="Months" CustomAttribute="February" />February<br />
   ...
</div>

The JSON that's built looks like:

{ January: 'on', February: 'on', ... }
Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
0

I know you're trying to turn a query-string into a JSON object, but perhaps you might be interested in going directly from an html form to a JSON object (without having to specify really convoluted name attributes). If so, check out JSONForms: http://code.google.com/p/jsonf/

Disclaimer: I started the jsonforms project. It's still young, but personally I think it rocks!

Jeoff Wilks
  • 363
  • 3
  • 7
  • That's cool, but doesn't solve the issue. I'm using ASP.NET, so with all of the name mangling done by having NamingContainers everywhere, your library won't create very friendly object property names. – Cᴏʀʏ Mar 04 '10 at 18:16
0

You can also use the parseQuery plugin to create an object from the serialized elements.

var contact = $.parseQuery($("#step1 *").serialize());
Alex
  • 721
  • 6
  • 11
0

You can traverse an object map using the $.each utility function.

$(function() {
    map = [{ January: 'on' },{ March: 'on' },{ September: 'on' }];
    $.each(map, function() {
       $.each(this, function(key, val) {alert(key + " = " + val);});;
    });
});

The first each gets you all array objects. With the second one you can get the key and value of your object.

kgiannakakis
  • 103,016
  • 27
  • 158
  • 194
  • I think this is a good start -- at least something I can work with. How about the situation where each object in the map can have the same name? It's not exactly a requirement, but I'd like to handle that case if it comes up in the future. The complexity seems to go up quite a bit with that. I'll take your example and start working on something though... – Cᴏʀʏ Apr 25 '09 at 22:21