Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NewtonSoft Json deserialization property list as array of objects

Tags:

.net

json.net

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?

like image 824
GrahamB Avatar asked Jul 18 '18 16:07

GrahamB


People also ask

Can JSON serialize a list?

Json.NET has excellent support for serializing and deserializing collections of objects. To serialize a collection - a generic list, array, dictionary, or your own custom collection - simply call the serializer with the object you want to get JSON for.

Is JObject parse slow?

That being said, it's known that parsing to JObject can be slower than deserializing -- see stackify.com/top-11-json-performance-usage-tips which states, Parsing generic JSON to a JSON.net JObject ... is slower (~20%) than reading that data in to a defined class type.

What is JsonProperty annotation C#?

JsonPropertyAttribute indicates that a property should be serialized when member serialization is set to opt-in. It includes non-public properties in serialization and deserialization. It can be used to customize type name, reference, null, and default value handling for the property value.


1 Answers

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

like image 186
Brian Rogers Avatar answered Oct 11 '22 03:10

Brian Rogers