First of, I would like to give a huge thanks to Phil Haack for his awesome blog post, Sending JSON to an ASP.NET MVC Action Method Argument, which shows how really flexible the ASP.NET MVC can be. Also, I owe him a beer next time I see him for saving my behind and providing an actual solution to my original POSTing JSON Data to MVC Controllers post.
Namaste, Phil!
Client-side JSON ‘Serialization’
In Phil’s post, he mentions that using a model binder falls short because of the lack of validation support:
There’s one key problem with using a model binder to accept JSON. By writing a custom model binder, you miss out on validation. Using his example, if you type “abc” for the Age
field, you will get a serialization failure when attempting to serialize the JSON into the PersonInputModel
object because Age is an Int32
and the serialization will fail.
He’s absolutely right and, as he mentions, you should use the JsonValueProviderFactory from the ASP.NET MVC 2 Futures library. Funny enough, as he was finishing up his blog post, I was working a simple JSON model binder to show how this is possible with what comes out of the box with MVC2. The source code for this sample is located here, http://github.com/jglozano/samples/tree/master/JSONModelBinder.
The interesting thing in both solutions, is that you do have to some work on the client side to take your javascript structures and convert them into JSON. Phil shows on his post how you can use a JSON plug-in for jQuery to accomplish this. If you read down in the comments, Dave Ward points out that we should be using Crockford’s json2.js to serialize (or stringify) your structure into valid JSON. Funny enough, while working on my sample I had bumped into this StackOverflow post that explains the reason to use Crockford’s script and quotes John Resig’s recommendation (add here for ease of read):
The second major feature of the language is the addition of native JSON support to the language.
I've been championing this move for a long time and I'm glad to see it finally arrive in a specification.
In the meantime PLEASE start migrating your JSON-using applications over to Crockford's json2.js. It is fully compatible with the ECMAScript 5 specification and gracefully degrades if a native (faster!) implementation exists.
In fact, I just landed a change in jQuery yesterday that utilizes the JSON.parse
method if it exists, now that it has been completely specified.
There are two primary methods for handling JSON: JSON.parse
(which converts a JSON string into a JavaScript object) and JSON.stringify
(which convert a JavaScript object into a serialized string).
Once this piece is applied on the client side, there is still some work to be done server side. For this piece, let’s assume we do not know about IValueProvider and the JsonValueProviderFactory and let’s explore the creation of a model binder (since I already have the code :P) and some of the issues of taking this approach instead of using the ‘baked-in’ pieces that Phil described.
JsonModelBinder and JavaScriptSerializer
I took a short look at implementing a JSONModelBinder and this is what I came up with:
The code is pretty simple and to the point, except for the interaction with the JavaScriptSerializer piece, which unfortunately does all the heavy lifting for the system. If you’re using .NET 4, the JavaScriptSerializer class has a Deserialize(String, Type) that returns an object of the specified type. However, in .NET 3.5, this method is not available since it’s marked as internal and the only option you have for deserialization is the DeserializeObject(String) method.
If you were to use this method for the example JSON data (in our case, {“Name”:”John User”, “Age”:”29”}) you will get an object of type IDictionary<string,object> as a result. If you look at Jonathan Carter’s implementation, he leverages the DictionaryValueProvider within the JsonValueProviderFactory, which works great with the new model binding pieces since this type implements IValueProvider.
So, in order to get this piece to work with the type that’s specified via the ModelMetadata, I had to do some work with reflection and extension methods to expose the correct Deserialize method:
Unfortunately, not straight forward but it at the end accomplishes the sending of JSON data to a controller action:
Where do we go from here?
Again, although this is functional, you will not get the desired results or leverage the full power of MVC2 new validation components. If you need to support this scenario, PLEASE USE the approach that Phil’s post outlines, it will save you headaches.
If you’re interested in looking at my code, it can be found here: http://github.com/jglozano/samples/tree/master/JSONModelBinder – also, you want to use this with .NET4, there are instructions within the model binder that tell you what to change in order to get things working correctly.
Happy Coding!