Jan 10

I’ve been making use of the JsonValueProviderFactory a lot lately. The JsonValueProviderFactory was released as part of MVC 3 and is a really easy way to introduce Json into your web application. In fact all MVC 3 websites support posting Json off the bat with no code changes. Phil Haack wrote an excellent blog post introducing the JsonValueProviderFactory.

One limitation I’ve found with the current JsonValueProviderFactory is when you have a property in your model that is a dynamic type. Unfortunately the JsonValueProviderFactory is unable to bind deserialized Json to a dynamic property. Example below:

My solution JsonDotNetValueProviderFactory

The current JsonValueProviderFactory implements the ValueProviderFactory abstract class and overrides the GetValueProvider method. The GetValueProvider method needs to return a IValueProvider. The JsonValueProviderFactory does this by returning a DictionaryValueProvider<object> which implements IValueProvider. The DictionaryValueProvider constructor takes a IDictionary<string, object> which populates the ValueProvider.

It’s the DictionaryValueProvider constructor taking IDictionary<string, object> which makes things easy for us. Why? The ExpandoObject implements IDictionary<string, object> all we need to do is deserialize Json to an ExpandoObject and we should have a solution. Fortunately the awesome Json.Net library has a ExpandoObjectConverter that will deserialize Json to an ExpandoObject. Below is the finished JsonDotNetValueProviderFactory using the ExpandoObjectConverter (I also reduced the code current JsonValueProviderFactory 73 lines JsonDotNetValueProviderFactory 27 lines):

Replacing the JsonValueProviderFactory

This is done in the Application_Start() method in Global.asax by removing the JsonValueProviderFactory from ValueProviderFactories.Factories and adding the JsonDotNetValueProviderFactory. Example below:

Conclusion

If you run into the same limitation with the JsonValueProvider as I did I hope you find this post useful. I’ve also written a small test harness MVC project that shows a model with a dynamic property not binding using the JsonValueProviderFactory and binding correctly using my JsonDotNetValueProviderFactory. The project also contains unit tests for the JsonDotNetValueProviderFactory. The test harness project is on GitHub https://github.com/DalSoft/JsonDotNetValueProviderFactory and can also be downloaded as a zip file.

Test harness

5 Responses to “ASP.NET MVC 3 – Improved JsonValueProviderFactory using Json.Net”

  1. This worked for me; to get the entire InputModel passed to the Controller action to be treated as dynamic I had to create a dummy wrapper class:

    public class JsonDynamicWrapper
    {
    public dynamic o { get; set; }
    }

    And send all data I want handled as dynamic like:

    $.ajax({

    data: { o: data },

    });

    So basically the o property is typed as dynamic, and the actual data is passed in this property from jquery.

    It might also be worth noting I find I get fewer squawks from MVC if I use the following when submitting to Asp.Net MVC:

    function postJson(url, data, success, error) {
    return $.ajax({
    url: url,
    data: JSON.stringify(data),
    type: ‘POST’,
    dataType: ‘json’,
    contentType: ‘application/json; charset=utf-8′,
    success: success,
    error: error
    });
    }

    So the contentType includes an explicit charset to avoid unicode issues from for example form input, and it returns the $.Deferred object the $.ajax() call returns so you can then use .done() and .fail() on the returned object if you like, or the older style success and error callbacks.

  2. Nate Smith says:

    Cool. Cheers for posting.

  3. Jon says:

    This is a really cool solution. I had been grappling with the dynamic property binding issue as well. And, Json.NET is simply a top notch framework, best thing that happened to ASPNET MVC imho.

  4. Pimboden says:

    This works for me, but only for Plain-Objects as soon as an Object has a Property that contains another object, this property is set to null
    Any help?

Leave a Reply

preload preload preload