Convert form data to JavaScript object with jQuery

1,059,095

Solution 1

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

function objectifyForm(formArray) {
    //serialize data function
    var returnArray = {};
    for (var i = 0; i < formArray.length; i++){
        returnArray[formArray[i]['name']] = formArray[i]['value'];
    }
    return returnArray;
}

Watch out for hidden fields which have the same name as real inputs as they will get overwritten.

Solution 2

Convert forms to JSON like a boss


The current source is on GitHub and Bower.

$ bower install jquery-serialize-object


The following code is now deprecated.

The following code can take work with all sorts of input names; and handle them just as you'd expect.

For example:

<!-- All of these will work! -->
<input name="honey[badger]" value="a">
<input name="wombat[]" value="b">
<input name="hello[panda][]" value="c">
<input name="animals[0][name]" value="d">
<input name="animals[0][breed]" value="e">
<input name="crazy[1][][wonky]" value="f">
<input name="dream[as][vividly][as][you][can]" value="g">
// Output
{
  "honey":{
    "badger":"a"
  },
  "wombat":["b"],
  "hello":{
    "panda":["c"]
  },
  "animals":[
    {
      "name":"d",
      "breed":"e"
    }
  ],
  "crazy":[
    null,
    [
      {"wonky":"f"}
    ]
  ],
  "dream":{
    "as":{
      "vividly":{
        "as":{
          "you":{
            "can":"g"
          }
        }
      }
    }
  }
}

Usage

$('#my-form').serializeObject();

The Sorcery (JavaScript)

(function($){
    $.fn.serializeObject = function(){

        var self = this,
            json = {},
            push_counters = {},
            patterns = {
                "validate": /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*$/,
                "key":      /[a-zA-Z0-9_]+|(?=\[\])/g,
                "push":     /^$/,
                "fixed":    /^\d+$/,
                "named":    /^[a-zA-Z0-9_]+$/
            };


        this.build = function(base, key, value){
            base[key] = value;
            return base;
        };

        this.push_counter = function(key){
            if(push_counters[key] === undefined){
                push_counters[key] = 0;
            }
            return push_counters[key]++;
        };

        $.each($(this).serializeArray(), function(){

            // Skip invalid keys
            if(!patterns.validate.test(this.name)){
                return;
            }

            var k,
                keys = this.name.match(patterns.key),
                merge = this.value,
                reverse_key = this.name;

            while((k = keys.pop()) !== undefined){

                // Adjust reverse_key
                reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');

                // Push
                if(k.match(patterns.push)){
                    merge = self.build([], self.push_counter(reverse_key), merge);
                }

                // Fixed
                else if(k.match(patterns.fixed)){
                    merge = self.build([], k, merge);
                }

                // Named
                else if(k.match(patterns.named)){
                    merge = self.build({}, k, merge);
                }
            }

            json = $.extend(true, json, merge);
        });

        return json;
    };
})(jQuery);

Solution 3

What's wrong with:

var data = {};
$(".form-selector").serializeArray().map(function(x){data[x.name] = x.value;}); 

Solution 4

A fixed version of Tobias Cohen's solution. This one correctly handles falsy values like 0 and ''.

jQuery.fn.serializeObject = function() {
  var arrayData, objectData;
  arrayData = this.serializeArray();
  objectData = {};

  $.each(arrayData, function() {
    var value;

    if (this.value != null) {
      value = this.value;
    } else {
      value = '';
    }

    if (objectData[this.name] != null) {
      if (!objectData[this.name].push) {
        objectData[this.name] = [objectData[this.name]];
      }

      objectData[this.name].push(value);
    } else {
      objectData[this.name] = value;
    }
  });

  return objectData;
};

And a CoffeeScript version for your coding convenience:

jQuery.fn.serializeObject = ->
  arrayData = @serializeArray()
  objectData = {}

  $.each arrayData, ->
    if @value?
      value = @value
    else
      value = ''

    if objectData[@name]?
      unless objectData[@name].push
        objectData[@name] = [objectData[@name]]

      objectData[@name].push value
    else
      objectData[@name] = value

  return objectData

Solution 5

I like using Array.prototype.reduce because it's a one-liner, and it doesn't rely on Underscore.js or the like:

$('#formid').serializeArray()
    .reduce(function(a, x) { a[x.name] = x.value; return a; }, {});

This is similar to the answer using Array.prototype.map, but you don't need to clutter up your scope with an additional object variable. One-stop shopping.

IMPORTANT NOTE: Forms with inputs that have duplicate name attributes are valid HTML, and is actually a common approach. Using any of the answers in this thread will be inappropriate in that case (since object keys must be unique).

Share:
1,059,095
Yisroel
Author by

Yisroel

Updated on October 27, 2021

Comments

  • Yisroel
    Yisroel over 2 years

    How do I convert all elements of my form to a JavaScript object?

    I'd like to have some way of automatically building a JavaScript 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();

    • Yisroel
      Yisroel almost 15 years
      because the first returns a string, exactly like what you'd get if you submitted the form with a GET method, and the second gives you a array of objects, each with a name value pair. I want that if i have a field named "email" i get an object that will allow me to retrieve that value with obj.email. With serializeArray(), i'd have to do something like obj[indexOfElement].value
  • Tobias Cohen
    Tobias Cohen almost 15 years
    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.
  • Samuel Meacham
    Samuel Meacham almost 14 years
    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.
  • joshperry
    joshperry over 13 years
    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.
  • frontendbeauty
    frontendbeauty over 12 years
    So, that works pretty well. But it's misnamed: it doesn't return JSON, as the name implies. Instead, it returns an object literal. Also, it's important to check for hasOwnProperty, otherwise your arrays have anything that's attached to their prototype, like: {numbers: ["1", "3", indexOf: function(){...}]}
  • maček
    maček almost 12 years
    it seems as though you didn't read my entire comment. I devised a solution that handles foo[bar]-type inputs as well as foo[bar][bof][a][b][c][etc]; see my answer in this thread. Also note that foo[bar] "parsing" is not unique to PHP. Rails heavily relies on this convention for passing form attributes to objects.
  • Andrew Barber
    Andrew Barber over 11 years
    Stack Overflow isn't here for you to promote your library.
  • pocesar
    pocesar over 11 years
    ok but the the serializeForm is part of my library and does exactly what the OP wants
  • Mulan
    Mulan about 11 years
    It is risky to eval user input - anything could happen. I strongly recommend to not do this.
  • Carlo Moretti
    Carlo Moretti over 10 years
    what are you trying to do?
  • Chris Baker
    Chris Baker over 10 years
    I used a variation of this for a very specific implementation, thanks a lot!
  • Șerban Ghiță
    Șerban Ghiță over 10 years
    Thanks for letting me know that it's useful. I have some incoming features to roll and this motivates me.
  • sites
    sites almost 9 years
    $(this).serializeArray().reduce(function(m,o){ m[o.name] = o.value; return m;}, {})
  • Alf Eaton
    Alf Eaton about 8 years
    This does seem to work nicely. There's an alternative repository for jquery-deparam that includes description files for bower and npm.
  • davidtgq
    davidtgq almost 8 years
    From what I can tell, the difference is that your solution does not depend on serializeArray so you have the freedom to choose whatever inputs you want (eg. you can include disabled inputs), right? I.e. this is not coupled to any form or the submit event, it's just independent by itself?
  • davidtgq
    davidtgq almost 8 years
    Couldn't you get around XSS attacks by converting them to JS object first, instead of directly to string?
  • sites
    sites almost 8 years
    the only small difference with linked answer is that there is no data needed to instantiate, reduce returns the object. This is not independent since toArray is from jQuery.
  • supertrue
    supertrue about 7 years
    I like this solution, but it doesn't handle form fields in key[] array format; [ {key: 'items[]', value: 1 }, {key: 'items[]', value: 2 } ] results in { items: { "": 2 } }.
  • Alex78191
    Alex78191 almost 7 years
    The last string is the best
  • Leonardo Beal
    Leonardo Beal about 6 years
    This answer does cover the case mentioned, but it does not cover cases like checkbox[] or even one[another][another_one]
  • Bhavik Hirani
    Bhavik Hirani about 6 years
    @LeonardoBeal i fix my ans .. check this now ..!
  • Adarsha
    Adarsha about 6 years
    Thank you.. this makes (as you mentioned) tiny but very important difference.
  • Jack G
    Jack G almost 5 years
    I am the downvoter and I downvoted because eval should not be used in this way because eval, when used in this way, promotes terribly unreliable, buggy, illperformant, and potentially unsecure practices.
  • iRaS
    iRaS over 4 years
    I can't agree this is a good answer. And please when you write answers make your code self-explanatory or explain it. this.c = function(k,v){ eval("c = typeof "+k+";"); if(c == 'undefined') _t.b(k,v);} is short und not explanatory. A dev with less experience will just copy this without understanding why and how it works.
  • Paflow
    Paflow about 4 years
    I get wrong result if I don't add a init-object to that function: $('form').serializeArray().reduce((o, p) => ({...o, [p.name]: p.value}), {})
  • Bhavik Hirani
    Bhavik Hirani almost 4 years
    @JackGiffin Check out my new code now because I've removed eval() from my code.
  • Burhan Kashour
    Burhan Kashour almost 3 years
    @BhavikHirani After a long search, I found your answer, you saved me a long hours of search! thanks man!!
  • Bhavik Hirani
    Bhavik Hirani almost 3 years
    @BurhanKashour welcome anytime please don't forget to upvote !
  • Nathan Chappell
    Nathan Chappell over 2 years
    $(this).serializeArray().reduce((o,kv) => ({...o, [kv.name]: kv.value}), {})
  • skilleo
    skilleo over 2 years
    doesn't handle nested form notation into json.
  • aret
    aret over 2 years
    Obviously as it is not considered as valid html html.spec.whatwg.org/multipage/forms.html#the-form-element, even chromium remove nested form
  • chichilatte
    chichilatte over 2 years
    Perfect answer.
  • rahman
    rahman over 2 years
    Thank you So Much😍, work for flat Model
  • alex
    alex over 2 years
    this is the one row solution, though others work too.
  • Nidecker
    Nidecker over 2 years
    Yes! So many upvoted answers, but only this one works properly with checkboxes. Thanks
  • Adam F
    Adam F almost 2 years
    You mean other than the fact that this doesn't support form arrays?
  • Aldis
    Aldis almost 2 years
    I am not certain how it worked previously, but now it does not. Changed a bit that works: Object.fromEntries(_.map($('#myform').serializeArray(), _.values))