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

13 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?

  5. Nick says:

    Hi DalSoft – is it possible to make it work with collections?

    • DalSoft says:

      It should already work with collections, post an example if your having problems and I’ll do my best to help.

      • Ixonal says:

        I’m seeing the same sort of problem. I pass in an object containing the two objects I actually want to use as my action parameters, I see the deserialization happen (seemingly correctly), but when the controller action runs, both parameters are null. Is there an intermediate step that’s missing to match up the property names with the parameter names?

        • DalSoft says:

          Hi post the code or a link to a gist and I’ll take a look. It’s working for me for nested classes.

          • Ixonal says:

            Well, let’s see, without getting too verbose, I’m sending a post request containing a serialization of the following object (very much simplified, but _this and geometry are both objects that may contain additional objects and so forth):

            { vm: _this, geometry: geometry }

            My controller action has parameters matching these:

            public ActionResult RequiresImageRequest(CriteriaViewModel vm, Geometry geometry) {

            }

            But both of thees parameters show up as null. I’ve looked in the factory at the converted expando object, and all of the values are there. The only thing I’ve seen that might be the culprit is that, while the outermost object is converted into the DictionaryValueProvider, the inner objects are still ExpandoObjects. Would thatcause it not to recognize them correctly?

          • Ixonal says:

            Actually, the reworking in this gist look to fix the issue:
            https://gist.github.com/DalSoft/1588818

          • DalSoft says:

            Thanks for posting back it looks like how you return a ValueProvider has changed after MVC 3. When I get some time I’ll look into it as I’m still sure you can use a ExpandoObject and hardly any code to do this. This is because a ExpandoObject can always be cast to a Dictionary.

  6. Ixonal says:

    Yes, the ExpandoObject works fine. I did post some modifications at the end of that gist, as it couldn’t handle lists as the base element before (casting issue).

Leave a Reply

preload preload preload