1

I have a set of, well, interestingly formed JSON that I need to read using C#, in my case specifically using JSON.NET although that could likely be done using System.Text.Json as well.

The data has a structure similar to this:

[
    {
        "Name": "Name A",
        "Value": "Apples"
    },
    {
        "Name": "Name B",
        "Value": {
            "key1": "value1",
            "key2": "value2"
        }
    }
]

In short, an array of objects of somewhat different shape, specifically the 'Value' property is varying. I am ONLY interested in records having Value data of key/value (Dictionary<string, string>) type, if that helps.

What is the best (simplest in this case) way to read this data into some kind of typed representation? I say typed as that is what I would like to work with but it is not strictly a requirement.

Something like "Hey JSON.NET, please give me all records where 'Value' is of type key/value, if possible as an object structure where Value is materialized as typed Dictionary<string, string> objects. Other records may be discarded if that helps. Thanks!".

It would be really great if this could somehow be implemented using POCO objects.

2
  • 2
    I'd personally just parse to a JArray (where each value will be a JObject) and then transform from that into the name/dictionary pairs you want. Commented Mar 8, 2022 at 19:22
  • Thanks, yes, that may be the way to go. If I am doing two passes I could probably use a typed structure where 'value' is object and transform into a target structure. At least I would get some things "for free" that way (the actual structure contains more data than in the example). Commented Mar 8, 2022 at 19:30

2 Answers 2

1

One possible way would be to use a JsonConverter subclass.

This could e.g. contain a Dictionary<string, string> with the key-value pairs if a JTokenType.Object is found and return an empty dictionary in any other case. Can of course be customized as desired.

DictionaryValueConverter

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class DictionaryValueConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Dictionary<string, string>));
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue,
        JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        return token.Type == JTokenType.Object 
            ? token.ToObject<Dictionary<string, string>>() 
            : new Dictionary<string, string>();
    }

    public override bool CanWrite => false;

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

Item

An Item of your array would then look like something like this:

using Newtonsoft.Json;

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

    [JsonConverter(typeof(DictionaryValueConverter))]
    public Dictionary<string, string> Value { get; set; }
}

Test

You could then call this something like this:

using Newtonsoft.Json;

public static class Program
{
    static void Main()
    {
        string json = @"
        [
            {
                ""Name"": ""Name A"",
                ""Value"": ""Apples""
            },
            {
                ""Name"": ""Name B"",
                ""Value"": {
                    ""key1"": ""value1"",
                    ""key2"": ""value2""
                }
            }
            ]
        ";

        var list = JsonConvert.DeserializeObject<List<Item>>(json);

        if (list == null) return;
        foreach (var item in list)
        {
            Console.WriteLine($"name: {item.Name}");
            if (item.Value.Count == 0)
            {
                Console.WriteLine("no value");
            }
            foreach (var (key, value) in item.Value)
            {
                Console.WriteLine($" -> Key = {key}, Value = {value}");
            }
            Console.WriteLine();
        }
    }
}

The output of the above example code would be:

name: Name A
no value

name: Name B
 -> Key = key1, Value = value1
 -> Key = key2, Value = value2

As mentioned above, the approach can be customized as desired.

Sign up to request clarification or add additional context in comments.

1 Comment

That is really neat! That is also a reusable approach which will come in handy since I will likely have to integrate 20-30 different services, all similar but different.
1

try this

    var jsonParsed = JArray.Parse(json); 

    var items = new List<Item>();

    foreach (JObject item in jsonParsed)
    {
        var name = string.Empty;
        foreach (var prop in item.Properties())
        {
            if (prop.Value.Type.ToString() != "Object")
            {
                if (prop.Name == "Name") name = (string)prop.Value;
                continue;
            }
            items.Add(new Item
            {
                Name = name,
                Value = prop.Value.ToObject<Dictionary<string, string>>()
            });
        }
    }

result

[
  {
    "Name": "Name B",
    "Value": {
      "key1": "value1",
      "key2": "value2"
    }
  }
]

class

public class Item
{
    public string Name { get; set; }
    public Dictionary<string, string> Value { get; set; }
}

1 Comment

Thanks, I picked parts of this and combined with other approaches since my actual use cases is a bit more complex than my example. Works well so far!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.