Post Complex JavaScript Objects to ASP.NET MVC Controllers

Published on Author nickriggs53 Comments

There is a lot on conversation going on about binding complex JavaScript objects to ASP.NET MVC actions. Complex objects are objects that have sub objects and/or arrays.

Let’s assume the following complex model:

I have a Person object with some properties, an array of phone numbers and an Address object. I would like to pass a JavaScript representation of this object to our Controller’s Create action:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person person)
{
    //Code to add the person goes here

    //return the view
    return View();
}

On the client, the JavaScript representation of a Person would be:

var myPerson = {
    FirstName: "Nick",
    LastName: "Riggs",
    Age: 29,
    Address: {
        Street: "2780 Somewhere Far",
        City: "Birmingham",
        State: "AL"
    },
    PhoneNumbers: [
        "205-555-5634",
        "205-555-2294",
        "205-555-7681"
    ]
};

One way to send this object to our Controller is to “stringify” the object into a JSON string using a plugin like toJSON. However, this requires us to change the Action to accept a string instead of a typed parameter, and then deserialize the string using the JavaScriptSerializer. I can get around this by automating the deserialization with a custom ActionFilterAttribute or ModelBinder. But, what if I want to use the built-in DefaultModelBinder functionality?

The default model binding in ASP.NET MVC works based on form post data. For example, if I were going to post a simple version of Person and have ASP.NET MVC map it to our action’s person parameter, I could post:

person.FirstName: Nick
person.LastName: Riggs
person.Age: 29

ASP.NET MVC does a good job of recognizing this post data as being a Person and mapping it as such. On top of that, it has its own simple yet powerful syntax for representing more complex objects, such as this:

person.FirstName: Nick
person.LastName: Riggs
person.Age: 29
person.PhoneNumbers[0]: 205-555-5634
person.PhoneNumbers[1]: 205-555-5634
person.PhoneNumbers[2]: 205-555-5634
person.Address.Street: 2780 Somewhere Far
person.Address.City: Birmingham
person.Address.State: AL

So, instead of stringifying my JavaScript objects, I will postify them! (I made the word postify™ up, it’s mine now). My custom postify plug-in will do the work. Here is the source code:

$.postify = function(value) {
    var result = {};

    var buildResult = function(object, prefix) {
        for (var key in object) {

            var postKey = isFinite(key)
                ? (prefix != "" ? prefix : "") + "[" + key + "]"
                : (prefix != "" ? prefix + "." : "") + key;

            switch (typeof (object[key])) {
                case "number": case "string": case "boolean":
                    result[postKey] = object[key];
                    break;

                case "object":
                    if (object[key].toUTCString)
                        result[postKey] = object[key].toUTCString().replace("UTC", "GMT");
                    else {
                        buildResult(object[key], postKey != "" ? postKey : key);
                    }
            }
        }
    };

    buildResult(value, "");

    return result;
};

This is the first cut of the plug-in, and I’m sure it’s missing something – I’ll update the source code as I make updates. That said, the plug-in greatly simplifies posting complex objects to ASP.NET MVC controllers. Here is a sample in jQuery that posts myPerson:

$.ajax({
    type: "POST",
    url: "/People/Create",
    data: $.postify(myPerson)
});

That’s it! The plugin will handle formatting the data in an ASP.NET MVC post-friendly manner. On the server side, the parameter inflates nicely using the default model binder:

Inflated-Person

If you need to post to an action that takes multiple parameters, the complex object must be prefixed with the name of the parameter – in our case, Person. To include another parameter, use this syntax:

$.ajax({
    type: "POST",
    url: "/JSON/DoSomething",
    data: $.postify({
        person: myPerson,
        otherParam: true
    })
});


Download Icon - Small Download Source Code

53 Responses to Post Complex JavaScript Objects to ASP.NET MVC Controllers

  1. […] Nick Riggs, Web Developer – Post Complex JavaScript Objects to ASP.NET MVC Controllers nickriggs.com/index.php/posts/post-complex-javascript-objects-to-asp-net-mvc-controllers – view page – cached Nick Riggs, Web Developer » Post Complex JavaScript Objects to ASP.NET MVC Controllers Comments Feed Nick Riggs, Web Developer Clean Up the setTimeout Function Simple AJAX Polling Plugin for jQuery — From the page […]

  2. Nick Riggs, Web Developer – Post Complex JavaScript Objects to ASP.NET MVC Controllers…

    Thank you for submitting this cool story – Trackback from progg.ru…

  3. You should use json2.js for this instead of rolling a custom function. Its JSON.stringify() does the same thing.

    The bonus when using json2.js is that browser-native JSON functionality in ECMAScript 3.1 mimics Crockford’s API. So, if your code is using json2.js’ JSON.stringify/parse, it will automatically use the faster native routines when the user’s running Firefox 3.5, IE 8, or the newest builds of Chrome/Safari.

  4. Definitely a cool alternative approach compared to creating your own ActionFilterAttribute (I wouldn’t even consider the stringify option 🙂 )

    I ask this not be be contrarian, but just a question…what does this method buy you that an ActionFilterAttribute does not? It’d be interesting to understand the performance implications. It seems the code to do this on the client vs. deserializing JSON on the server is rather similar.

  5. @Dave: While I like json2.js, this post was meant to address “postifing” a JavaScript object so that it works within ASP.NET MVC’s default model binding. Which is something json2.js doesn’t address.

  6. Nick, you’re absolutely right. I’m so used to POSTing JSON strings to ASMX/WCF services, I misunderstood what you were trying to do. Sorry about that.

    Out of curiosity, have you had trouble when supplying the JSON object directly to jQuery in the data parameter? If the data parameter is an object, jQuery will automatically construct and POST the appropriate serialization of the object. So far, I haven’t run into a DTO object that jQuery’s auto-serialization didn’t properly hand off to MVC’s model binder.

  7. Hey Nick, this is really good stuff. I’d like to use it in some of my own projects. Would you please consider attaching a license to this code to make this easier?

  8. You helped me work though a problem, but I favor a simpler solution than a new plug in –
    I add a string “NameField” to my ViewModel and then adjust the name of my Serializable object to that name:
    Model.NameField = “Family.Children[” + i.ToString() + “]”;

    easy peasy!

  9. Thanks for the post. I am using it a lot and it really made my code simpler.

    There is a bug that makes it break when one of the properties is null.

    I changed it as follows.

    if (object[key])
    {
    if (object[key].toUTCString)
    {
    result[postKey] = object[key].toUTCString().replace(“UTC”, “GMT”);
    }
    else
    {
    buildResult(object[key], postKey != “” ? postKey : key);
    }
    }

  10. Thanks, I’ve been trying for hours to figure out how to post back multiple complex objects without writing a new model binding object for every unique request.

    • The first thing I would try is something like:

      $.post(url, { "object1": $.postify(object1), "object2": $.postify(object2) });
      

      assuming a controller action like:

      public ActionResult DoStuff(ViewModel1 object1, ViewModel2 object2)
      

      Let me know how that goes.

  11. I can’t tell you how often I’ve turned to this plugin to solve brain dead model binding in MVC. Thanks for the quick solution that has saved many dev’s hours of screwing around with other more complex solutions for the same issue.

    <3

  12. Thanks – Nick! This is cool and indeed a very good workaround. I found one issue, if the object properties has null value it is throwing an exception. I fixed it locally as
    if (object[key] == null || object[key] == undefined)
    result[postKey] = object[key];
    else if (object[key].toUTCString)
    result[postKey] = object[key].toUTCString().replace(“UTC”, “GMT”);
    else …

  13. We just spent almost two hours trying to figure out why MVC would not interpret arrays in an object. We finally figured it out it was the “dots” and then we googled for the missing dot problem and found this site. We made one small change to allow for null objects, but then it worked!!! This is awesome!!! Thank you so much.

  14. Hey there, merely observe your own blog site through The search engines, and located that must be actually beneficial. I’m gonna be careful intended for the town. Let me get pleasure from if you progress this particular later on. Various other folks will more likely taken advantage of your publishing. Cheers!

  15. Hey There. I discovered your blog using msn. That is a really smartly written article.
    I will make sure to bookmark it and return to read extra of your helpful info.
    Thank you for the post. I’ll definitely comeback.

  16. This is the right web site for anybody who would like to find
    out about this topic. You realize a whole lot its almost tough to argue with
    you (not that I actually will need to…HaHa). You definitely put a new spin on a
    subject that has been written about for a long time. Excellent
    stuff, just wonderful!

Leave a Reply

Your email address will not be published. Required fields are marked *