Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deserialize JSON with dynamic and static key names in C#

I have to deserialize a response from an api which has the following structure:

[
  {
    "starttime": "...",
    "endtime": "....",
    "var1": {},
    "var2": {}
  },
  {
    "starttime": "...",
    "endtime": "....",
    "var1": {},
    "var3": {}
  },
  {
    "starttime": "...",
    "endtime": "....",
    "var1": {}
  }
] 

Some insights:

  • The JSON is an array of objects
  • Every object inside the array will ALWAYS have the properties "starttime" and "endtime"
  • Objects "var1", "var2", "var3" will ALWAYS have the same properties inside them... but the problem is that the object keys (var1, var2 or var3) are dynamic. It can be any string, and also the amount of this kind of objects is dynamic (I could have 3, or zero "var" objects).

I was thinking something like this, to deserialize the JSON string into a List of objects with properties "starttime", "endtime" and a dictionary with all the "var" objects.

public class MyResponse
{
    [JsonProperty(PropertyName = "starttime")]
    public string StartTime { get; set; }
    [JsonProperty(PropertyName = "endtime")]
    public string EndTime { get; set; }
    public Dictionary<string, VarObject> VarData { get; set; }
}

But the VarData property is always null.

Has anyone tried something like this?

like image 414
fPecc Avatar asked Oct 15 '22 09:10

fPecc


1 Answers

You have two options, the first is to deserialise directly to a List<Dictionary<string, object>>, for example:

var responses = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(json);

Alternatively, if you are stuck on using your object, you will need to write a custom converter. For example, something like this:

public class MyResponseConverter : JsonConverter
{
    public override bool CanConvert(Type type) => type == typeof(MyResponse);

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

        MyResponse response = new MyResponse
        {
            StartTime = (string)responseObject["starttime"],
            EndTime = (string)responseObject["endtime"],
        };

        var varData = new Dictionary<string, object>();

        foreach (var property in responseObject.Properties())
        {
            if(property.Name == "starttime" || property.Name == "endtime")
            {
                continue;
            }

            varData.Add(property.Name, property.Value);
        }

        response.VarData = varData;
        return response;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // If you want to write to JSON, you will need to implement this method
        throw new NotImplementedException();
    }
}

And your class would change slightly to this:

[JsonConverter(typeof(MyResponseConverter))]
public class MyResponse
{
    [JsonProperty(PropertyName = "starttime")]
    public string StartTime { get; set; }
    [JsonProperty(PropertyName = "endtime")]
    public string EndTime { get; set; }

    public Dictionary<string, object> VarData { get; set; }
}

Now you deserialise like this:

var responses = JsonConvert.DeserializeObject<List<MyResponse>>(json);
like image 72
DavidG Avatar answered Oct 20 '22 23:10

DavidG