30

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.

pbz
  • 8,865
  • 14
  • 56
  • 70

5 Answers5

34

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.

Boo
  • 1,634
  • 3
  • 20
  • 29
  • 1
    The list of dictionaries works; thanks for that. Since the change I'm trying to make is part of a bigger library I'm trying to avoid having to write custom object initialization. I'm looking into writing a custom converter for my class (SomeData2 in your example). If I can't get it to work then I'll use your List of Dictionary method. Thanks. – pbz Apr 03 '13 at 15:29
  • Thanks alot for this answer. I was having same kind of json with array with key value. I just used public IEnumerable> Data { get; set; } for that field and worked like charm.. my whole json was parsed fine!.. – maths Apr 16 '15 at 19:35
  • Note that this will not conserve the order of the original JSON - since a Dictionary will be sorted by the Key – Wouter Van Ranst Mar 04 '23 at 19:44
4

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.

Tom el Safadi
  • 6,164
  • 5
  • 49
  • 102
1

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; }
}
pbz
  • 8,865
  • 14
  • 56
  • 70
0
public class Datum
{
    public string key1 { get; set; }
    public string key2 { get; set; }
}

public class RootObject
{
    public string id { get; set; }
    public List<Datum> data { get; set; }
}

i used this wizard as well json2csharp.com to generate class for deserialized

for using that

using RestSharp;
using Newtonsoft.Json;

IRestResponse restSharp= callRestGetMethodby_restSharp(api_server_url);
string jsonString= restSharp.Content;

RootObject rootObj= JsonConvert.DeserializeObject<RootObject>(jsonString);
return Json(rootObj);

if you call rest by restsharp

    public IRestResponse callRestGetMethodby_restSharp(string API_URL)
    {
        var client = new RestSharp.RestClient(API_URL);
        var request = new RestRequest(Method.GET);
        request.AddHeader("Content-Type", "application/json");
        request.AddHeader("cache-control", "no-cache");
        IRestResponse response = client.Execute(request);
        return response;
    }

also you can get this 6 line of restsharp from getpostman.com tools

saber tabatabaee yazdi
  • 4,404
  • 3
  • 42
  • 58
0

You can use public IEnumerable<IDictionary<string, string>> Data as the most answers are recommending, but it is not the best idea, since it creates a new dictionary for each array key value item. I recommend to use List<KeyValuePair<string, string>> instead ( or you can create a custom class as well instead of using KeyValuePair )

var json = "[{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }]";

List<SomeData> listSomeData = JsonConvert.DeserializeObject<List<SomeData>>(json);

public class SomeData
{
    [JsonProperty("id")]
    public string Id { get; set; }

    public List<KeyValuePair<string, string>> Data { get; set; }

    [JsonConstructor]
    public SomeData(JArray data)
    {
        Data = data.Select(d => new KeyValuePair<string, string>(
        ((JObject)d).Properties().First().Name,
        ((JObject)d).Properties().First().Value.ToString()))
        .ToList();
    }
}
Serge
  • 40,935
  • 4
  • 18
  • 45