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.
CodePudding user response:
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; }
}
CodePudding user response:
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.