Reputation:
For a minute please ignore the travesty that is this complicated model, and assume you are stuck with it :)
public class Foo
{
public FooList Foos{get;set;}
}
public class FooList
{
public object SomeBar {get; set;}
}
public class Bar
{
public string Name {get;set;}
}
public class AnotherBar
{
public bool IsCrazy{get;set;}
}
So we will give the Action a Foo
and the foo will have a FooList
Which contians either Bar
or AnotherBar
. What SomeBar contains doesn't really matter..it is an object of a few KNOWN types, but we don't know which one...oh and they don't inherit from a common ancestor.
Ok, so when you post json to an action that takes a Foo. the Foo object will be deserialized, but FooList is null. If I create a custom ModelBinder, manually read in the stream and tell JsonConvert to deserialize it. everything works
From what I understand MVC4 uses JsonConvert under the covers. What is the difference here? Why would the built-in deserialization not work and me explicitly using JsonConvert does?
I am probably going to have to bounty this, and I plan on putting as much as I can (500?)
ps. I am not asking for a work around. I really just want to know what is going on to cause the behavior.
Now for the code:
//some sample JSON: {"Foos":{"SomeBar":{"Name":"Insanity"}}}
public ActionResult Test(Foo foo)
{
//Foo.FooList is null
return new EmptyResult();
}
public ActionResult BinderTest([ModelBinder(typeof(FooModelBinder))] Foo foo)
{
//Everything is as it should be
return new EmptyResult();
}
public class FooModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
request.InputStream.Seek(0, SeekOrigin.Begin);
var jsonString = new StreamReader(request.InputStream).ReadToEnd();
Foo foo = JsonConvert.DeserializeObject<Foo>(jsonString);
return foo;
}
}
And some client code
$(function () {
$('#button1').click(function () {
var data = '{"Foos":{"SomeBar":{"Name":"Insanity"}}}';
$.post('/home/bindertest', data);
});
$('#button2').click(function () {
var data = '{"Foos":{"SomeBar":{"Name":"Insanity"}}}';
$.post('/home/test', data);
});
});
Upvotes: 1
Views: 962
Reputation: 129707
The default serializer for Web API controllers (classes inheriting from ApiController
) in MVC 4 is Json.Net. However, I believe the default serializer for standard MVC 4 controllers (classes inheriting from Controller
) is still the JavaScriptSerializer
(for backward compatibility reasons). See this question. So that could very well explain the difference in behavior.
Upvotes: 2