Search code examples
c#asp.net-mvc-3nullablemodel-binding

MVC 3 doesn't bind nullable long


I made a test website to debug an issue I'm having, and it appears that either I'm passing in the JSON data wrong or MVC just can't bind nullable longs. I'm using the latest MVC 3 release, of course.

public class GetDataModel
{
    public string TestString { get; set; }
    public long? TestLong { get; set; }
    public int? TestInt { get; set; }
}

[HttpPost]
public ActionResult GetData(GetDataModel model)
{
    // Do stuff
}

I'm posting a JSON string with the correct JSON content type:

{ "TestString":"test", "TestLong":12345, "TestInt":123 }

The long isn't bound, it's always null. It works if I put the value in quotes, but I shouldn't have to do that, should I? Do I need to have a custom model binder for that value?


Solution

  • My colleague came up with a workaround for this. The solution is to take the input stream and use a Regex to wrap all numeric variables in quotes to trick the JavaScriptSerializer into deserialising the longs properly. It's not a perfect solution, but it takes care of the issue.

    This is done in a custom model binder. I used Posting JSON Data to ASP.NET MVC as an example. You have to take care, though, if the input stream is accessed anywhere else.

    public class JsonModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (!IsJSONRequest(controllerContext))
                return base.BindModel(controllerContext, bindingContext);
    
            // Get the JSON data that's been posted
            var jsonStringData = new StreamReader(controllerContext.HttpContext.Request.InputStream).ReadToEnd();
    
            // Wrap numerics
            jsonStringData = Regex.Replace(jsonStringData, @"(?<=:)\s{0,4}(?<num>[\d\.]+)\s{0,4}(?=[,|\]|\}]+)", "\"${num}\"");
    
            // Use the built-in serializer to do the work for us
            return new JavaScriptSerializer().Deserialize(jsonStringData, bindingContext.ModelMetadata.ModelType);
        }
    
        private static bool IsJSONRequest(ControllerContext controllerContext)
        {
            var contentType = controllerContext.HttpContext.Request.ContentType;
            return contentType.Contains("application/json");
        }
    }
    

    Then put this in the Global:

    ModelBinders.Binders.DefaultBinder = new JsonModelBinder();
    

    Now the long gets bound successfully. I would call this a bug in the JavaScriptSerializer. Also note that arrays of longs or nullable longs get bound just fine without the quotes.