user60456
user60456

Reputation:

Why isn't this nested model being deserialized as I expect?

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

Answers (1)

Brian Rogers
Brian Rogers

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

Related Questions