Why is my Web API controller action not deserializing child properties of child properties?

sonicblis picture sonicblis · Aug 14, 2014 · Viewed 15.6k times · Source

I'm sending a json payload in a PUT request to a web API controller action. The action in question has a signature that looks like this:

public IHttpActionResult Post([FromBody]SaveThingRequest request)

SaveThingRequest looks something like this:

public class SaveThingRequest
{
    public List<ElementInfo> Elements { get; set; }

    public class ElementInfo
    {
        public List<ElementSettingInfo> Settings { get; set; }

        public class ElementSettingInfo
        {
            public string Name { get; set; }
            public string Value { get; set; }
        }
    }
}

I'm posting json in the body of the request that contains Elements that have Settings. I've confirmed this by manually deserializing in the controller action and confirming that the JSON has a structure that looks something like:

{
    Elements: [
        {
            Settings: [
                {
                    Name: 'Name 1',
                    Value: 'Value 1'
                },
                {
                    Name: 'Name 2',
                    Value: 'Value 2'
                }
            ]
        },
        {
            Settings: [
                {
                    Name: 'Name 1',
                    Value: 'Value 1'
                },
                {
                    Name: 'Name 2',
                    Value: 'Value 2'
                }
            ]
        }
    ]
}

However, when .NET deserializes the payload and creates the SaveThingRequest, my Elements are populated but all of them have a null Settings property. I don't know how else to troubleshoot this. Does anyone have any thoughts on what might be going on here?

Answer

sonicblis picture sonicblis · Aug 14, 2014

This question should be deleted. It works as advertised. My problem was that I had an additional property on the JSON called 'settings' (lower-case) that the deserializer was trying to match because NewtonSoft deserialization attempts a non-case sensitive match if a case sensitive one isn't found. I was able to discover what was happening by changing the signiture of the method to:

public IHttpActionResult Post([FromBody]object request)

and then adding this to the method implementation:

var result = JsonConvert.DeserializeObject<SaveThingRequest>(request.ToString());

I got a deserialization exception saying:

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Noteable.Contracts.ClinicalReports.SaveThingRequest+ElementInfo+ElementSettingInfo]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'Elements[0].settings.TextSize', line 11, position 20.

The end of the message showed me what was being deserialized when the deserializer failed and pointed me in the right direction. =[