Dear reader of Juri's TechBlog,
I moved my blog to a new domain and a new hosting solution as well. I'm now blogging on MVC3: Doesn't Deserialize Nullable Properties from Json

A couple of days ago I noticed that the Nullable properties of a C# object were not properly deserialized when posting back a Json object to an MVC3 action. It seems like this is a bug in the JavaScriptSerializer used by MVC3 to serialize/deserialize Json data.
This behavior is quite nerving as you probably have a lot of nullable properties, often resulting directly from the underlying data structure in the DB (especially in a data-base first case with EF). So one solution might be to create your separate ViewModels and somehow handle the mapping of the nullable properties through those. Extremely smelly!

While browsing for a potential solution I came across this blogpost. According to the author of the post, apparently the DataContractSerializer doesn't have this problem. So what he does is to create an MVC3 ActionFilter for intercepting the call to the Action method and then to deserialize the arriving Json data using the DataContractSerializer.
public class JsonModelBinder : ActionFilterAttribute
    public JsonModelBinder()
    public Type ActionParameterType { get; set; }
    public string ActionParameterName { get; set; }
    public override void OnActionExecuting(ActionExecutingContext filterContext)
        HttpRequestBase request = filterContext.HttpContext.Request;
        Stream stream = request.InputStream;
        stream.Position = 0;
        filterContext.ActionParameters[ActionParameterName] =
                        (new DataContractJsonSerializer(ActionParameterType)).ReadObject(stream);
The real action method then looks as follows:
[JsonModelBinder(ActionParameterName="student", ActionParameterType=typeof(Student)]
public ActionResult Save(Student student)
    // save the student 
    return View();
Now, I didn't try "Syper blogger"'s approach, also because I don't really want all of the devs to require to put those attributes on every action method they write. This is a huge overhead and introduces redundancy in the type definition etc.

So I aimed for a more clean approach by implementing a custom IModelBinder and by using the popular (and even more performant) library for deserializing my entities. So the result looked like
public class MyCustomModelBinder: IModelBinder
    private IModelBinder fallbackModelBinder;

    public MyCustomModelBinder(IModelBinder fallbackModelBinder)
        this.fallbackModelBinder = fallbackModelBinder;

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        if (controllerContext.HttpContext.Request.HttpMethod.Equals("GET", StringComparison.InvariantCultureIgnoreCase))
            return this.fallbackModelBinder.BindModel(controllerContext, bindingContext);
            //ONLY do this for POST,PUT,DELETE requests that transmit application/json
            string bodyText;
            using (var stream = controllerContext.HttpContext.Request.InputStream)
                stream.Seek(0, SeekOrigin.Begin);
                using (var reader = new StreamReader(stream))
                    bodyText = reader.ReadToEnd();

            if (string.IsNullOrEmpty(bodyText))
                return (null);

            return JsonConvert.DeserializeObject(bodyText, bindingContext.ModelType);
Inside global.asax you have then to register the IModelBinder as follows:
protected void Application_Start()
   ModelBinders.Binders.DefaultBinder = new MyCustomModelBinder(ModelBinders.Binders.DefaultBinder);
Note that I'm only touching POST,PUT or DELETE requests that post Json data to my MVC3 webapp. All other requests are directly forwarded to the default model binder.

I prefer this solution of the registration of ActionFilter attributes on each action method because in this way the deserialization process is completely transparent. Let me know if any better solution comes to your mind for solving this issue.

Posts you might also be interested in..

Credits: Hoctro | Jack Book


Anonymous said...

Have you tested this code??

Juri Strumpflohner said...

Sure, doesn't it work in your case?

Dan said...

Not only does it work as is, but it allows be to bind to a model with a nested List.

You, sir, are the man!

Enis Pilavdzic said...

This post seems very useful except I agree with anonymous, that code doesn't even compile. missing ) on line 12 for example.

Then after I fix that, I get:
JsonReaderException was unhandled by user code
Error reading string. Unexpected token: StartObject. Line 1, position 1.

Juri Strumpflohner said...

@Pilavdzic Thx, fixed the missing parentesis, was a typo when copying the code onto the blog post.

Regarding the JsonReaderException, could you provide some more details, i.e.
- the type of request
- the Json payload?

Darick said...

Nice article, thanks. One thing I did was check to see if the contentType is json:

if (controllerContext.HttpContext.Request.ContentType.Contains("application/json"))

then deserialize, otherwise just return the fallback. That would help with errors where json isn't being sent.

Juri Strumpflohner said...

@Darick: In fact, in my production version I'm using, I already fixed that. Will update the post accordingly, thx.

Post a Comment