6

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?

fPecc
  • 115
  • 7
  • Does that help: https://stackoverflow.com/questions/47043028/serialize-deserialize-dynamic-property-name-using-json-net? – MakePeaceGreatAgain Nov 20 '19 at 11:54
  • 3
    You can deserialise the entire JSON as `List>` – DavidG Nov 20 '19 at 11:55
  • @DavidG yes, that would work, but then I need to access all properties (even the starttime and endtime, which I know will always be there) as a key of a dictionary... this does not seem like the cleanest solution. – fPecc Nov 20 '19 at 12:10
  • 1
    It may not be particularly clean, but your JSON isn't particularly clean in that regard either. – DavidG Nov 20 '19 at 12:11
  • @HimBromBeere so you are saying to make my own Converter? In the example you sent, inside of the ReadJson method, the guy already knows that his property will be named "type". My problem is that I dont know the name, it could be "var1", or it could be "pepe" – fPecc Nov 20 '19 at 12:13
  • `TypedExtensionDataConverter` from [How to deserialize a child object with dynamic (numeric) key names?](https://stackoverflow.com/a/40094403/3744182) might so the job. – dbc Nov 20 '19 at 19:37

2 Answers2

4

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);
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • Thanks @DavidG, I implemented something really similar to this and it worked like a charm – fPecc Nov 20 '19 at 18:47
0

You can view example Here https://dotnetfiddle.net/QgXWQi.

But for more flexibility, this logic is better to implement in method that marked as [OnDeserialized]

Like here https://www.newtonsoft.com/json/help/html/SerializationAttributes.htm

The main idea is to parse it as JObject and then convert to Dictionary

    var jObj = JObject.Parse(jsonText);
    var varData = jObj
        .Children<JProperty>()
        .Where(p => p.Name  != "starttime" && p.Name  != "endtime")
        .ToDictionary(x=> x.Name, x => ((JObject)x.Value).ToObject<VarObject>());
TemaTre
  • 1,422
  • 2
  • 12
  • 20