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: // Example of a model that won't work with the current JsonValueProviderFactory but will work with JsonDotNetValueProviderFactory public class CmsViewModel { public bool IsVisible { get; set; } public string Content { get; set; } public DateTime Modified { get; set; } public DateTime Created { get; set; } // This property will not work with the current JsonValueProviderFactory public dynamic UserDefined { get; set; } }

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 an

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): using System.Dynamic; using System.Globalization; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace System.Web.Mvc { public sealed class JsonDotNetValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { if (controllerContext == null) throw new ArgumentNullException("controllerContext"); if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) return null; var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream); var bodyText = reader.ReadToEnd(); return String.IsNullOrEmpty(bodyText) ? null : new DictionaryValueProvider<object>(JsonConvert.DeserializeObject<ExpandoObject>(bodyText, new ExpandoObjectConverter()), CultureInfo.CurrentCulture); } } }

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: using System.Linq; using System.Web.Mvc; using System.Web.Routing; namespace JsonDotNetValueProviderFactoryTestHarness { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "TestJsonValueProviderFactory", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); //Remove and JsonValueProviderFactory and add JsonDotNetValueProviderFactory ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault()); ValueProviderFactories.Factories.Add(new JsonDotNetValueProviderFactory()); } } }

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