6

If I have the following form:

<form>
  <input name="foo" value="bar">
  <input name="hello" value="world">
  <input name="animals[]" value="panda">
  <input name="animals[]" value="koala">
  <input name="car[make]" value="Honda">
  <input name="car[origin]" value="Japan">
</form>

I do not want to use $("form").serialize():

foo=bar&hello=world&animals%5B%5D=panda&animals%5B%5D=koalacar&%5Bmake%5D=Honda&car%5Borigin%5D=Japan

Instead, I want this:

{"foo":"bar", "hello":"world", "animals":["panda", "koala"], "car":{"make":"Honda", "origin":"Japan"}}

To my understanding, jQuery used to do this, but they switched the serialize method to return the GET-style query string. Is there an easy way to get my desired result?


EDIT

I've updated my original question to include car[make] and car[origin] examples. It should be assumed that foo[bar][baz] or foo[bar][baz][bof] input could appear on the form as well.

Additionally, numerically indexed keys that are specified such as foo[0]=a, foo[1]=b, foo[4]=c should be preserved, e.g.,

{ ... "foo":["a", "b", undefined, undefined, "c"] ... }
2
  • If I get you right, .serializeArray() is what you're looking for (api.jquery.com/serializeArray). Commented Nov 30, 2011 at 17:24
  • @Quasdunk, .serializeArray() does not return the result as specified in my question. Commented Nov 30, 2011 at 17:50

5 Answers 5

9

Convert forms to JSON LIKE A BOSS

Github: Follow along on Github

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

E.g.,

<!-- 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

(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);
Sign up to request clarification or add additional context in comments.

2 Comments

This looks like it would work correctly with the default asp.net MVC model binding, is that correct?
@Benjamin, that's a try-it-and-see, for me; I'm not too familiar with ASP.NET
6

You could use ".serializeArray()", and then fix the result:

var json = {};
$.each($('form').serializeArray(), function() {
  json[this.name] = this.value;
});

Of course you might want to worry about multi-valued fields:

var json = {};
$.each($('form').serializeArray(), function() {
  var cur = json[this.name];
  if (cur !== undefined) {
    if ($.isArray(cur))
      cur.push(this.value);
    else
      json[ this.name.replace(/\[[^\]]*\]$/, '') ] = [ cur, this.value ];
  }
  else
    json[this.name] = this.value;
});

(edit — now that I think about it, "serialize" and "serializeArray" already kind-of deal with multi-value parameters for you, giving you names like "whatever[2]" in the serialized form. It'd work anyway but it might be unnecessary to do anything more than the simple one.)

6 Comments

This creates json like { ... "animals[]":["panda", "koala"]} where animals[] should be animals.
uhh ... huh? Oh wait; yes I see what you mean. I'll fix that.
OK I added a regex to strip those.
Thanks for working with me on this, I'm still looking for a bit more of a comprehensive solution though. See edits above. I did some digging around on this and found a lot of half-assed solutions to this problem. I suspect this is going to end up with a bounty.
Well a lot depends on what your server-side code expects to see in the HTTP request.
|
1

This is a very good answer to convert the querystring to JSON. I got from the following SO link

knovel.util = {
     queryParamsToJson : function (theURL) {
        href = theURL;
        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;
    }
}

Hope this helps some one..

1 Comment

If you want your blank fields to come out as blank fields rather than NaN, change the line that reads value = isNaN(a[1]) ? a[1] : parseFloat(a[1]); to simply value = a[1]
0

I always do it like this, maybe a bit long winded but it's easy for me to maintain.

function FormContent(foo, hello, animals)
{
    this.Foo = foo;
    this.Hello = hello;
    this.Animals = animals;
}

var animals = [];
$("[name='animals[]']").each(function(){
    animals.push($(this).val());
});

var foo = $("[name='foo']").val();
var hello = $("[name='hello']").val();

var formContent = new FormContent(foo, hello, animals);
var jsonContent = json.stringify(formContent); // from json.org

I just realized you specifically asked "using jQuery" sorry. I've never had luck serializing to json with jQuery. Hope this helps anyway.

2 Comments

I appreciate you trying to help, but this answer is very inflexible and requires reconfiguration for each form/input I use it on.
I posted a solution here that I think might be useful to you.
0

In my case, I had to take a form I had previously serialized using jQuery and convert it into an object, replacing all encoding for special characters, etc. with their literal value. Using Dilip's example here, this is what I used.

  Form1 = $('#MyForm').serialize();  // Name=Mr.+John+Mark+Jr.&Email=john%40example.com
  Form1 = Form1.replace(/\+/g, '%20'); // Name=Mr.%20John%20Mark%20Jr.&Email=john%40example.com
  var Decoded = decodeURIComponent(Form1); // Name=Mr. John Mark Jr.&[email protected]
  var Values = queryParamsToJson(Decoded); // { Name : "Mr. John Mark Jr.", Email : "[email protected]" }

  function queryParamsToJson(theURL)
  {
     href = theURL;
     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;
  }

Now I can access any value as "Values.variable"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.