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

17.1k Views Asked by At

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?

5

There are 5 best solutions below

7
On BEST ANSWER

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]string request)

and then adding this to the method implementation:

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

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. =[

0
On

I had the same problem that the child property was null. However the problem was something else.

I had only a getter on the property, once I added a setter as well, then the property was set as expected.

1
On

I was stuck in the same problem. However, the problem was something else.

In my case, the property that was not getting serialize was declared without public access specifier. I declared it to public, and it ran perfectly fine.

Hope this will help someone who got stuck in this situation. It took me 20 mins to reach this solution.

0
On

Make sure you didn't do something stupid like I did and sent a partially 'stringified' JSON string from the browser instead of a JSON object.

See the problem? I didn't at first until I tried deserializing it from a string and then I realized that shippingAddress itself was a string instead of being an actual JSON object. The rest of the object was proper JSON, but I accidentally serialized the shippingAddress field.

{ "fspID": 571285, "foo": 444, "shippingAddress": "{\"countryCode\":\"US\",\"firstName\":\"Test User\",\"lastName\":null,\"address1\":\"1 Main St4\",\"address2\":null,\"company\":null,\"city\":\"San Jose\",\"stateOrProvince\":\"California\",\"stateCd\":\"CA\",\"zipOrPostal\":\"95131\",\"countryName\":\"United States\",\"countryDesc\":\"United States\",\"phone\":null,\"phone2\":null}" }

0
On

If you are using JSON.NET and the attribute [JsonProperty] to name a property – make sure you haven't accidentally copy pasted the same property twice and forgotten to update the string value.

 [JsonProperty("billingAddress")]
 public Address BillingAddress { get;set; }

 [JsonProperty("billingAddress")]
 public Address ShippingAddress { get;set; }

This will throw a Newtonsoft.Json.JsonSerializationException that won't actually be visible to you and will screw up the model.