GrahamB
GrahamB

Reputation: 1428

NewtonSoft Json deserialization property list as array of objects

I have an odd Json result set that has a repeating (but changeable) property that I need to convert to an array of an object type, e.g.

"result": {
    "documents": {
        "abcd": {
            "propertyX": 0
            "propertyY": "A"
        },
        "efgh": {
            "propertyX": 5
            "propertyY": "B"
        },
        "ijkl": {
            "propertyX": 2
            "propertyY": "C"
        }
    }
}

What I'd like to do is to have my Result object with a document property, and this have an array of "items". Each item object will contain "propertyX", "propertyY" etc. Unfortunately "abcd", "efgh" etc. are a random list of items but they are rendered as distinct properties.

Is there a straightforward way of handling this or would I need a custom converter?

Upvotes: 10

Views: 6233

Answers (1)

Brian Rogers
Brian Rogers

Reputation: 129687

Yes, the straightforward way to handle this is to use a Dictionary<string, Item> for your documents property. The random document names would become the keys of the dictionary. You can declare the classes like this:

class RootObject
{
    public Result Result { get; set; }
}

class Result
{
    public Dictionary<string, Item> Documents { get; set; }
}

class Item
{
    public string PropertyX { get; set; }
    public string PropertyY { get; set; }
}

Then deserialize the JSON like this:

RootObject root = JsonConvert.DeserializeObject<RootObject>(json);

Fiddle: https://dotnetfiddle.net/lTDGj3


If you do not want a dictionary in your class and instead would really rather have an array (or list) of items, then yes, you would need a converter. In that case, you would declare your classes like this:

class RootObject
{
    public Result Result { get; set; }
}

class Result
{
    [JsonConverter(typeof(DocumentListConverter))]
    public List<Item> Documents { get; set; }
}

class Item
{
    public string Name { get; set; }
    public string PropertyX { get; set; }
    public string PropertyY { get; set; }
}

The custom converter class for the document list would look something like this:

class DocumentListConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(List<Item>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        List<Item> items = new List<Item>();
        foreach (JProperty prop in jo.Properties())
        {
            Item item = prop.Value.ToObject<Item>();
            item.Name = prop.Name;
            items.Add(item);
        }
        return items;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

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

And you would deserialize in the same way as before:

RootObject root = JsonConvert.DeserializeObject<RootObject>(json);

Fiddle: https://dotnetfiddle.net/xWRMGP

Upvotes: 14

Related Questions