Evan Frisch
Evan Frisch

Reputation: 1374

Deserialize Dictionary with unknown keys with RestSharp

I currently am getting a JSON object with a shape similar to the following:

{
 more data here...
  "years": {
    "value": 2013,
    "item1": {
      "total": 0.1044,
      "Low": 0.0143,
      "Mid": 0.1044,
      "High": 0.3524,
      "min": 0.0143,
      "max": 0.3524,
    },
    "item2": {
      "total": 0.1702,
      "Low": 0.167,
      "Mid": 0.1702,
      "High": 0.1737,
      "min": 0.167,
      "max": 0.1737,
    },...
  }
}

I unfortunately, have no control over the shape of the JSON.

I am trying to get RestSharp to deserialize this into an object where Item1, Item2, and the rest fill into a Dictionary I currently have the following code:

public class Year
{
    public int Value { get; set; }
    public Dictionary<string, Data> Data { get; set; }
}

public class Data
{
    public decimal Total { get; set; }
    public decimal Low { get; set; }
    public decimal Mid { get; set; }
    public decimal High { get; set; }
    public decimal Min { get; set; }
    public decimal Max { get; set; }
}

And I am hoping to get Item1, Item2, etc. to be the keys of the Dictionary and the values underneath to fill in the Data class as the value of the Dictionary. But it isn't working at the moment (the rest of my structure is, it's just this innermost part). Am I just approaching the structure wrong? I want to avoid having to create a specific class for Item1 and Item2.

Upvotes: 2

Views: 1141

Answers (2)

Jeff
Jeff

Reputation: 7674

You could use a custom JsonConverter:

public class YearConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Year);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = JObject.Load(reader);

        int year = (int)obj["value"];
        var data = new Dictionary<string, Data>();

        foreach (var dataItem in obj.Children()
            .OfType<JProperty>()
            .Where(p => p.Name.StartsWith("item")))
        {
            data.Add(dataItem.Name, dataItem.Value.ToObject<Data>());
        }

        return new Year
        {
            Value = year,
            Data = data
        };
    }

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

Decorate your Year class with the converter:

[JsonConverter(typeof(YearConverter))]
public class Year
{
    public int Value { get; set; }
    public Dictionary<string, Data> Data { get; set; }
}

Using it like this, for example:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
{
  ""value"": 2013,
  ""item1"": {
    ""total"": 0.1044,
    ""Low"": 0.0143,
    ""Mid"": 0.1044,
    ""High"": 0.3524,
    ""min"": 0.0143,
    ""max"": 0.3524,
  },
  ""item2"": {
    ""total"": 0.1702,
    ""Low"": 0.167,
    ""Mid"": 0.1702,
    ""High"": 0.1737,
    ""min"": 0.167,
    ""max"": 0.1737,
  }
}";
        var years = JsonConvert.DeserializeObject<Year>(json);
    }
}

Upvotes: 1

Chris
Chris

Reputation: 322

Steps: 1. Always shape your JSON object in a logical way

This issue would be significantly easier to handle if you restructured your json object in a more logical way. Right now you have n number of data objects at the same level as "value" and you are changing their property names dynamically.

Personally, I would reshape the json as such:

{
   "years":[
      {
         "value":2013,
         "data":[
            {
               "name":"item1",
               "total":0.1044,
               "Low":0.0143,
               "Mid":0.1044,
               "High":0.3524,
               "min":0.0143,
               "max":0.3524
            },
            {
               "name":"item2",
               "total":0.1702,
               "Low":0.167,
               "Mid":0.1702,
               "High":0.1737,
               "min":0.167,
               "max":0.1737
            }
         ]
      }
   ]
}

Step 2: Add the Newtonsoft.Json attributes to your classes

This allows for easy deserialization

public class Base
{
    [JsonProperty("years")]
    public List<Year> years { get; set; }
}

    public class Year
    {
        [JsonProperty("value")]
        public int Value { get; set; }

        [JsonProperty("data")]
        public List<Data> data { get; set; }
    }

    public class Data
{
        [JsonProperty("name")]
    public string name { get; set; }
        [JsonProperty("total")]
        public decimal Total { get; set; }
        [JsonProperty("Low")]
        public decimal Low { get; set; }
        [JsonProperty("Mid")]
        public decimal Mid { get; set; }
        [JsonProperty("High")]
        public decimal High { get; set; }
        [JsonProperty("min")]
        public decimal Min { get; set; }
        [JsonProperty("max")]
        public decimal Max { get; set; }
    }

Step 3: Deserialize it

Base myYears = JsonConvert.DeserializeObject<Base>(myJsonString);

Upvotes: 1

Related Questions