1

I am trying to deserialize a structure that contains one item, with multiple items in it, that in turn potentially contain single or multiple items inside it... Here is the example of JSON:

"Content": {
    "title": {
        "$type": "EntityModelData",
        "Id": "1133",
        "Content": {
            "intro": "Example Introduction",
            "title": "Example Title",
        }
    },
    "sections": {
        "$type": "ContentModelData[]",
        "$values": [
            {
                "title": {
                    "$type": "EntityModelData",
                    "Id": "1232",
                    "Content": {
                        "title": "Section 1 (Single-value context)"
                    }
                },
                "context": {
                    "$type": "ContentModelData",
                    "publicationType": {
                        "$type": "KeywordModelData",
                        "Id": "1175",
                    }
                }
            },
            {
                "title": {
                    "$type": "EntityModelData",
                    "Id": "1234",
                    "Content": {
                        "title": "Section 2 (Multi-valued context)"
                    }
                },
                "context": {
                    "$type": "ContentModelData[]",
                    "$values": [
                        {
                            "publicationType": {
                                "$type": "KeywordModelData",
                                "Id": "1182",
                            },
                            "contentType": {
                                "$type": "KeywordModelData",
                                "Id": "1166",
                            }
                        },
                        {
                            "publicationType": {
                                "$type": "KeywordModelData",
                                "Id": "1182",
                            },
                            "contentType": {
                                "$type": "KeywordModelData",
                                "Id": "1238",
                            }
                        }
                    ]
                }
            }
        ]
    }
}

I have set up my classes as follows...

public class ContentModel
{
    [JsonProperty("Content")]
    public Content Content { get; set; }
}

public class Content
{
    [JsonProperty("title")]
    public TitleModel Title { get; set; }

    [JsonProperty("sections")]
    public List<Sections> Sections { get; set; }
}

//[JsonArrayAttribute("sections")] (Putting this in makes it fail!)
public class Sections
{
    [JsonProperty("title")]
    public TitleModel Title { get; set; }

    //[JsonProperty("context")]
    //public Context Context { get; set; }

    //[JsonProperty("context")]
    //public IList<Context> Contexts { get; set; }
}

public class Context
{
    //[JsonProperty("$type")]
    //public string Type { get; set; }

    //[JsonProperty("publicationType")]
    //public Keyword PublicationType { get; set; }

    //[JsonProperty("contentType")]
    //public Keyword ContentType { get; set; }

    //[JsonProperty("subject")]
    //public Keyword Subject { get; set; }
}

Note, some of it is commented out, as it breaks when I include or try further deserialize the context-items (single/multi)

The deserialize command I am using is...

var pageContent = JObject.Parse([[above-content-that has a lot more in it]]);
foreach (var entity in pageContent.SelectToken(".Parent[0].ContanerForContent[[which has the above JSON it it]]"))
{
    var content = entity.ToObject<ContentModel>().Content; break;
}

Now, this is currently successful at deserializing up to the title!!! (with the comments that I have above in the code) ... I can loop through a list of "sections" and get into their "Title" ... but when I try to include the context (which is both single-valued and multi-valued... it throws the error below...

1.

List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'Regions[0].Entities[1].Content.sections.$values[2].context.$values', line 1, position 5038.'

2.

List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'Regions[0].Entities[1].Content.sections.$values', line 1, position 4214.'

3.

Newtonsoft.Json.JsonSerializationException: 
'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Context]' 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<T>) 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 'Regions[0].Entities[1].Content.guidebookSections.$values[0].context.publicationType', line 1, position 4271.'

I have tried a few different ways, but keep getting variations of the error at different levels.

At this point - I think I am going about this the wrong way completely. How do I deserialize the "context" items?

  • `"$type"` is Json.NET's type indicator property for polymorphic models. Some background: [TypeNameHandling setting](https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm), [Json.net serialize/deserialize derived types?](https://stackoverflow.com/q/8513042/3744182), [TypeNameHandling caution in Newtonsoft Json](https://stackoverflow.com/q/39565954/3744182) and [Using a custom type discriminator to tell JSON.net which type of a class hierarchy to deserialize](https://stackoverflow.com/q/11099466/3744182). Do you know the schema for the model being serialized? – dbc Mar 12 '20 at 20:15
  • Thank-you for the direction. Looked at links, tried to change but I think I can't use the "type" in JSON "ContentModelData"/"ContentModelData[]" (my example is a sub-set and that ContentModelData "type" can be used many different areas within my result (list of images/properties/cars/colors with very different properties... or perhaps I am just not seeing it... Would you be able to please elaborate more with my example perhaps? – Bradley Pennington Mar 12 '20 at 21:40

1 Answers1

1

Thanks for the inputs above... all of them were educational, and I eventually found one that worked for me...

public class SingleValueArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);

        if (jo["$type"].Value<string>().Contains("[]"))
        {
            return jo.ToObject<List<T>>(serializer);
        }
        else
        {
            return new List<T>(new[] { jo.ToObject<T>(serializer) });
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

and then on my properties where I could get either a list or a single item... I add this:

[JsonProperty("context")]
[JsonConverter(typeof(SingleValueArrayConverter<Context>))]
public List<Context> Context { get; set; }