JSON, ASP.NET MVC and JQuery – Working with Untyped JSON made easy


image

In this post, we’ll create a JsonValue binder for ASP.NET MVC3, to parse incoming Json to the JsonValue implementation to access/modify values in an untyped way, using ‘dynamic’ features (See the examples below).

Note: ASP.NET MVC3 already supports binding incoming Json to typed parameters, read more about that in Scott’s post. This post is more towards untyped, server side Json manipulation scenarios.

A Quick Preface on JsonValue Implementation

Few months ago, Glenn Block posted about the new WCF web API stack, and in his PDC talks, he demonstrated the new Json primitives with ‘dynamic’ support.

To summarize, this allows you to manipulate Json data using a dynamic wrapper, as shown in Glenn’s post.

[ServiceContract]
  
  //A method in your service
  public JsonValue Post(JsonValue contact)
    {
        var postedContact = (dynamic)contact;
        var contactResponse = (dynamic)new JsonObject();
        contactResponse.Name = postedContact.Name;
        contactResponse.ContactId = nextId++;
        return contactResponse;
    }

From Glenn’s post

In the snippet above you can see that the Post method accepts a JsonValue and returns a JsonValue. Within it casts the incoming parameter to dynamic (there actually is an extension method AsDynamic which you can use) pulls out the name and then creates a new JsonObject which it sets some properties on and returns.

I’m a big fan of fluent wrappers on top of data formats like Json and Xml (See my previous ElasticObject implementation). The new JsonValue class comes with a bunch of goodness, Read the documentation here, and have a look at the actual implementation here

 

Creating a JsonValue binder for ASP.NET MVC

Now, Let us see how to create a model binder for MVC, to accept Json data you may post from the client side using JQuery. The idea is to to parse the same to the JsonValue object and hand over the same to a parameter of an Action in your controller. This will enable you to leverage the goodness of JsonValue for untyped scenarios, from your ASP.NET MVC controllers.

At this point, you may fire up your Visual Studio, and start a new ASP.NET MVC3 application. Next, you need to add references to the Microsoft.Runtime.Serialization.Json library. You can do this either by downloading the codebase from http://wcf.codeplex.com, or you can get going easily by adding a package reference to WcfWebApis from your MVC project, using nuget. Just goto View->Other Windows->Package Manager console, and type

install-package WcfWebApis

You may also read more about installing packages via Package Manager Console

Once you’ve the package reference added – You can put together a model binder in your MVC project to parse the incoming Json, to return a JsonValue object as shown. Read more about Modelbinders here if required

So, here is our DynamicJsonBinder which parses the incoming Json data to a JsonValue instance.

  public class DynamicJsonBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {

            if (!controllerContext.HttpContext.Request.ContentType.StartsWith
                ("application/json", StringComparison.OrdinalIgnoreCase))
            {
                // not JSON request
                return null;
            }

            var inpStream = controllerContext.HttpContext.Request.InputStream;
            inpStream.Seek(0, SeekOrigin.Begin);

            StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
            string bodyText = reader.ReadToEnd();
            reader.Close();


            if (String.IsNullOrEmpty(bodyText))
            {
                // no JSON data
                return null;
            }

            return JsonValue.Parse(bodyText);
        }

    }

We’ll also need a custom model binder attribute, to tie the above Modelbinder to an action parameter of a given Action in a controller.

    public class DynamicJsonAttribute : CustomModelBinderAttribute
    {
        public override IModelBinder GetBinder()
        {
            return new DynamicJsonBinder();
        }
    }

Once you’ve the Model and ModelBinder ready, add a method/action in any of your MVC project Controllers. We’ll create a simple action that’ll accept a product and will return a list of suppliers, all using JSON.

        public ActionResult GetSuppliers([DynamicJson] JsonValue product)
        {

            var prod = product.AsDynamic();

            //Assume you are getting suppliers from a document database
            //based on prod.name
            var sup = JsonValue.Parse(@"[{""name"":""Supplier1""},
                                    {""name"":""Supplier2""}]");
            prod.suppliers = sup;

            //Return the modified json object
            return Content(product.ToString(), "application/json");
        }

So, I just added a GetSuppliers action to my Home controller, which takes a JsonValue object. Note that we are marking our action parameter with the [DynamicJson] attribute we created above – to ensure that our DynamicJsonBinder is invoked to bind the incoming Json data to the action parameter.

Also, we are manipulating the JsonValue object in our controller (just attaching some suppliers), and returns the same as Json with out any friction. You may note how easily this will go with JSON data sources, like document dbs. JsonValue implementation also supports Linq to Json, see the documentation on JsonValue here in Codeplex.

The Client Side

Ok, now we are ready to post a product to our GetSuppliers method, and get the result. We’ll just make a JQuery ajax post to our HomeController’s GetSuppliers action, with a product, when the user clicks the button btnGetSuppliers.

    <script type="text/javascript">

        $(document).ready(function () {
            var product = { name: "Pizza" };
            $("#btnGetSuppliers").click(function () {
                $("#prods").html("");
                $.ajax({
                    url: "/Home/GetSuppliers",
                    type: "POST",
                    dataType: "json",
                    contentType: 'application/json',
                    data: JSON.stringify(product),
                    success: function (response) {
                        $("#prodtemplate").tmpl(response).appendTo("#prods");
                    }
                });
            });

            $("#prodtemplate").tmpl(product).appendTo("#prods");

        });
    
    </script>

And you may also notice in the above code that, up on success, we are rendering the response using the JQuery Template plugin, to the prods div.

A template contains markup with binding expressions. The template is applied to data objects or arrays, and rendered into the HTML DOM.

In this case, remember that we modified our Json object in our Controller’s action to attach suppliers to it and then returned the Json – So the data that you’ll be getting back from your controller action may contain suppliers. We’ll render the Product using the template below. You may read more on how to use JQuery template’s tmpl() function to render templates here. The related html and the template is given below.

<input type="button" id="btnGetSuppliers" value="Send JQuery" />
<div id="prods" />


 <script id="prodtemplate" type="text/x-jquery-tmpl">
        <div>
           Name: ${name}<br/>
           {{if suppliers}}
           Suppliers:
           <ul>
           {{each(i,supplier) suppliers}}
            <li> ${supplier.name}</li>
           {{/each}}
           </ul> 
           {{/if}}
        </div>
    </script>

So, to conclude – We just saw how easy it is to work with Json data in ASP.NET MVC, especially leveraging the new JsonValue implementation. And this has some very interesting possibilities that I’ll explore later.

You may also read my previous posts on ASP.NET MVC and JQuery if you are interested.