Given the following json:
[ {"id":"123", ... "data":[{"key1":"val1"}, {"key2":"val2"}], ...}, ... ]
that is part of a bigger tree, how can I deserialize the "data" property into:
List<MyCustomClass> Data { get; set; }
or
List<KeyValuePair> Data { get; set; }
or
Dictionary<string, string> Data { get; set; }
using Json.NET? Either version will do (I prefer List of MyCustomClass though). I already have a class that contains other properties, like this:
public class SomeData
{
   [JsonProperty("_id")]
   public string Id { get; set; }
   ...
   public List<MyCustomClass> Data { get; set; }
}
where "MyCustomClass" would include just two properties (Key and Value). I noticed there is a KeyValuePairConverter class that sounds like it would do what I need, but I wasn't able to find an example on how to use it. Thanks.
The simplest way is deserialize array of key-value pairs to IDictionary<string, string>:
public class SomeData
{
    public string Id { get; set; }
    public IEnumerable<IDictionary<string, string>> Data { get; set; }
}
private static void Main(string[] args)
{
    var json = "{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }";
    var obj = JsonConvert.DeserializeObject<SomeData>(json);
}
But if you need deserialize that to your own class, it can be looks like that:
public class SomeData2
{
    public string Id { get; set; }
    public List<SomeDataPair> Data { get; set; }
}
public class SomeDataPair
{
    public string Key { get; set; }
    public string Value { get; set; }
}
private static void Main(string[] args)
{
    var json = "{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }";
    var rawObj = JObject.Parse(json);
    var obj2 = new SomeData2
    {
        Id = (string)rawObj["id"],
        Data = new List<SomeDataPair>()
    };
    foreach (var item in rawObj["data"])
    {
        foreach (var prop in item)
        {
            var property = prop as JProperty;
            if (property != null)
            {
                obj2.Data.Add(new SomeDataPair() { Key = property.Name, Value = property.Value.ToString() });
            }
        }
    }
}
See that I khow that Value is string and i call ToString() method, there can be another complex class.
Thanks @Boo for your answer but in my case I needed to take some small adjustements. This is how my JSON looks like:
{
    "rates": {
        "CAD": 1.5649,
        "CZK": 26.118,
        ...
    },
    "base": "EUR",
    "date": "2020-08-16"
}
And my DTO looks like the following:
public IDictionary<string, decimal> Rates { get; set; }
public string Base { get; set; }
public DateTime Date { get; set; }
So the only adjustement was to remove the IEnumerable around the IDictionary.
I ended up doing this:
[JsonConverter(typeof(MyCustomClassConverter))]
public class MyCustomClass
{
  internal class MyCustomClassConverter : JsonConverter
  {
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
      throw new NotImplementedException();
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
      JObject jObject = JObject.Load(reader);
      foreach (var prop in jObject)
      {
        return new MyCustomClass { Key = prop.Key, Value = prop.Value.ToString() };
      }
      return null;
    }
    public override bool CanConvert(Type objectType)
    {
      return typeof(MyCustomClass).IsAssignableFrom(objectType);
    }
  }
  public string Key { get; set; }
  public string Value { get; set; }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With