views:

14315

answers:

8

How do I serialize all elements of my form to a JSON object?

I'd like to have some way of automatically building a JSON object from my form, without having to loop over each element. I do not want a string, as returned by $('#formid').serialize();, nor do I want the map returned by $('#formid').serializeArray();

+2  A: 

There really is no way to do this without examining each of the elements. What you really want to know is "has someone else already written a method that converts a form to a JSON object?" Something like the following should work -- note that it will only give you the form elements that would be returned via a POST (must have a name). This is not tested.

function formToJSON( selector )
{
     var form = {};
     $(selector).find(':input[name]:enabled').each( function() {
         var self = $(this);
         var name = self.attr('name');
         if (form[name]) {
            form[name] = form[name] + ',' + self.val();
         }
         else {
            form[name] = self.val();
         }
     });

     return form;
}
tvanfosson
true, i'd love a plugin that would do this for me. the limitation of having a name is no big deal. will this pull all fields in a form, including textareas and selects?
Yisroel
not sure if you want to account for [disabled] but I don't think that should be sent/picked up.
meder
might be simpler to use serializeArray() to get the map, and then use code similar to the above to convert it a normal JSON object, this way i'm not dealing with the form itself
Yisroel
Using the serializedArray would work, but essentially you'd be iterating over the collection twice -- once to produce the array, then over the array. I don't see the need for that.
tvanfosson
I'll adjust the selector for enabled/disabled.
tvanfosson
+1  A: 

If you are trying to convert all form fields to json in order to submit this form through ajax here is jquery form plugin that does that.

serg
how do you get a JSON object using that plugin?
Yisroel
Well, if you need only json then you can look at plugin source and there is formToArray() function that seems like does form to json conversion. It is not documented in API though.
serg
+1  A: 

I wouldn't use this on a live site due to XSS attacks and probably plenty of other issues, but here's a quick example of what you could do:

$("#myform").submit(function(){
    var arr = $(this).serializeArray();
    var json = "";
    jQuery.each(arr, function(){
     jQuery.each(this, function(i, val){
      if (i=="name") {
       json += '"' + val + '":';
      } else if (i=="value") {
       json += '"' + val.replace(/"/g, '\\"') + '",';
      }
     });
    });
    json = "{" + json.substring(0, json.length - 1) + "}";
    // do something with json
    return false;
});
Jason Berry
+24  A: 

serializeArray already does exactly that, you just need to massage the data into your required format:

$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name]) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};

Example of this in action: http://tobiascohen.com/files/stackoverflow/jquery-form-serializeObject.html

Tobias Cohen
as tvanfosson says, why iterate over the collection twice?
Yisroel
Do you mean "why use serializeArray to get the data in the first place?" Because serializeArray is already written, is unit tested in multiple browsers, and could theoretically be improved in later versions of jQuery. The less code you write that has to access inconsistent things like DOM elements directly, the more stable your code will be.
Tobias Cohen
Thanks Tobias, was looking for a best practice.
Matt Gardner
Be warned, serializeArray() will not include disabled elements. I often disable input elements that are sync'd to other elements on the page, but I still want them included in my serialized object. You're better off using something like `$.map( $("#container :input"), function(n, i) { /* n.name and $(n).val() */ } );` if you need to include disabled elements.
Samuel Meacham
I think you need to add this to your code just after each statement:<pre>this.name=this.name.replace('[]','');</pre> if not, then there would be error in parsing JSON.
Morteza M.
A: 

form ....
type=checkbox name=setor_[] ... type=checkbox name=city_[] ...
--- serialize ---- json = { ..., 'setor_[]':["rio", "spa"], 'city_[]':"spa"}

city have only one checked, typeof equal string. I have need city typeof equal array, than update function to:
A solution in jQuery to this?
-------------------
....
for(var idx in o){
o[idx] = (idx.search(/(\[])$/) != -1 && typeOf(o[idx]) == 'string' )? new Array(o[idx]) : o[idx];
} return o; };

json = { ..., 'setor
[]':["rio", "spa"], 'city_[]':["spa"]}

Andre
A: 

I prefer this approach because: you don't have to iterate over 2 collections, you can get at things other than "name" and "value" if you need to, and you can sanitize your values before you store them in the object (if you have default values that you don't wish to store, for example).

$.formObject = function($o) {
    var o = {},
        real_value = function($field) {
            var val = $field.val() || "";

            // additional cleaning here, if needed

            return val;
        };

    if (typeof o != "object") {
        $o = $(o);
    }

    $(":input[name]", $o).each(function(i, field) {
        var $field = $(field),
            name = $field.attr("name"),
            value = real_value($field);

        if (o[name]) {
            if (!$.isArray(o[name])) {
                o[name] = [o[name]];
            }

            o[name].push(value);
        }

        else {
            o[name] = value;
        }
    });

    return o;
}

Use like so:

var obj = $.formObject($("#someForm"));

Only tested in Firefox.

kflorence
+4  A: 

Ok, I know this already has a highly upvoted answer, but another similar question was asked recently, and I was directed to this question as well. I'd like to offer my solution as well, because it offers an advantage over the accepted solution: You can include disabled form elements (which is sometimes important, depending on how your UI functions)

Here is my answer from the other SO question:

Initially, we were using jQuery's serializeArray() method, but that does not include form elements that are disabled. We will often disable form elements that are "sync'd" to other sources on the page, but we still need to include the data in our serialized object. So serializeArray() is out. We used the :input selector to get all input elements (both enabled and disabled) in a given container, and then $.map() to create our object.

var inputs = $("#container :input");
var obj = $.map(inputs, function(n, i)
{
    var o = {};
    o[n.name] = $(n).val();
    return o;
});
console.log(obj);

Note that for this to work, each of your inputs will need a name attribute, which will be the name of the property of the resulting object.

That is actually slightly modified from what we used. We needed to create an object that was structured as a .NET IDictionary, so we used this: (I provide it here in case it's useful)

var obj = $.map(inputs, function(n, i)
{
    return { Key: n.name, Value: $(n).val() };
});
console.log(obj);

I like both of these solutions, because they are simple uses of the $.map() function, and you have complete control over your selector (so, which elements you end up including in your resulting object). Also, no extra plugin required. Plain old jQuery.

Samuel Meacham
I tried this in a project, using `map` like this creates an array of objects with a single property, it does not collapse the properties all into one object.
joshperry
+2  A: 

Check this lib (not a jQuery, but its small and does exaclty what you're need): http://code.google.com/p/form2js/

Max