Tell don't ask

May 26, 2010

Posting an Array of Complex types using JQuery, JSON to ASP.NET MVC

Filed under: Uncategorized — telldontask @ 1:34 pm

I recently hit a requirement to take an array of objects and post them to an asp.net mvc action using JQuery’s ajax functionality.

I had to do a bit of Googling to work out how to do this so figured I’d document what I found.

The main two parts to this are getting JQuery to spit out JSON data and ASP.NET MVC to deserialize that data into a strongly typed generic list.

Javascript (JQuery)

  1. $('#saveChanges').click(function () {
  2.     var rules = new Array();
  3.  
  4.     $('#assignedRules table.ruleTable tbody tr').each(function (i) {
  5.         var subRuleCode = $('#subRuleCode', this).val();
  6.         var productGroupId = $('#productGroupId', this).val();
  7.         rules.push(new Rule(subRuleCode, productGroupId));
  8.     });
  9.  
  10.     var postData = JSON.stringify(rules);
  11.  
  12.     $.ajax({
  13.         url: '/rule/SaveUserRules',
  14.         data: postData,
  15.         contentType: 'application/json',
  16.         dataType: 'json',
  17.         type: 'post',
  18.         success: onRulesSaved,
  19.         error: function (data, textStatus) { alert(textStatus); }
  20.     }
  21.     );
  22. });

This javascript

  • creates a new array
  • iterates over some table rows and grabs a couple of hidden field values for each row
  • stores the hidden field values in a javascript class called Rule
  • pushes the Rule instance into the array
  • calls JSON.stringify to construct a valid JSON string from our array of Rules
  • posts the JSON string to our destination URL

As you can see, this script effectively translates a table of values, to an array of Rules which are then sent as JSON to our target URL.

The stringify function uses the script available from here.

The Rule javascript class is simple enough…

  1. function Rule(subRuleCode, productGroupId) {
  2.     this.SubRule = subRuleCode;
  3.     this.ProductGroupId = productGroupId;
  4. }

MVC Action

The next trick is to get the MVC action to accept the array and turn it into a strongly typed generic list.

  1. [AcceptVerbs(HttpVerbs.Post)]
  2. public JsonResult SaveUserRules(List<Rule> usersRules)
  3. {
  4.     // do something here
  5. }

As you can see we simply specify our generic list as the parameter for our action (which accepts HTTP Posts only).

JSON Model Binder

The final piece in the jigsaw is to tell MVC how to bind the JSON data to this generic list of Rules.

I found the code to create a simple JSON model binder here.

  1. public class JsonModelBinder : DefaultModelBinder
  2. {
  3.     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  4.     {
  5.         if (!IsJSONRequest(controllerContext))
  6.         {
  7.             return base.BindModel(controllerContext, bindingContext);
  8.         }
  9.  
  10.         // get the JSON data that's been posted
  11.         var request = controllerContext.HttpContext.Request;
  12.         var jsonStringData = new StreamReader(request.InputStream).ReadToEnd();
  13.  
  14.         return new JavaScriptSerializer()
  15.             .Deserialize(jsonStringData, bindingContext.ModelMetadata.ModelType);
  16.     }
  17.  
  18.     static bool IsJSONRequest(ControllerContext controllerContext)
  19.     {
  20.         var contentType = controllerContext.HttpContext.Request.ContentType;
  21.         return contentType.Contains("application/json");
  22.     }
  23. }

Now all you have to do is register this as the default model binder. Because it inherits the DefaultBinder, and only kicks in if the request type is application/json this shouldn’t break any non-json model binding.

  1. void AddModelBinders()
  2. {
  3.     ModelBinders.Binders.DefaultBinder = new JsonModelBinder();
  4. }

3 Comments »

  1. [...] to VotePosting an Array of Complex types using JQuery, JSON to ASP.NET MVC (5/26/2010)Wednesday, May 26, 2010 from telldontaskI recently hit a requirement to take an array of objects and [...]

    Pingback by ASP.NET MVC Archived Blog Posts, Page 1 — June 10, 2010 @ 2:44 am | Reply

  2. Finally – a post that puts it all together! Hurray! :)

    Comment by scott — March 10, 2011 @ 4:56 pm | Reply

  3. For some reason I had to reset my InputStream. I kept getting empty jsonStringData.

    This fixed my issue for anyone suffering the same:

    var request = controllerContext.HttpContext.Request;
    StreamReader reader = new StreamReader(request.InputStream);
    request.InputStream.Seek(0, 0); <—–
    string jsonStringData = reader.ReadToEnd();

    Comment by James — December 14, 2012 @ 5:00 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: