Deserialize a name as a value using Newtonsoft.Json

Viewed 84

Is it possible to deserialize a name as a value?

I've got some JSON like this:

{
  "id": "106906",
  "data": {
    "SER1": {
      "events": [
        {
          "timestamp": "2020-11-11T10:28:40.728600Z",
          "type": "high"
        }
      ]
    },
    "SER2": {
      "events": [
        {
          "timestamp": "2020-11-11T10:28:40.728600Z",
          "type": "high"
        }
      ]
    }
  }
}

The C# classes I'm using are like this:

// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse); 

public class Event    
{
    public DateTime timestamp { get; set; } 
    public string type { get; set; } 
}

public class Device    
{
    public string Id { get; set; } 
    public List<Event> events { get; set; } 
}

public class Data    
{
    public List<Device> Devices { get; set; } 
}

public class Root
{
    public string id { get; set; } 
    public Data data { get; set; } 
}

I want SER1 and SER2 to map to List<Device> Devices, with the Id of Device getting SER1 and SER2 respectively.

Is this possible?

1 Answers

Yes, this can be done, but you will need a custom JsonConverter to handle it.

First, you need to make a small adjustment to your classes, as you have an extra layer in there. There is no devices member in the JSON -- there is only data, and that represents the list of devices. So, change your root class to this:

public class Root
{
    public string Id { get; set; }

    [JsonProperty("data"), JsonConverter(typeof(DeviceListConverter))]
    public List<Device> Devices { get; set; }
}

You can also get rid of the Data class, as it is not needed.

Here is the code you will need for the custom DeviceListConverter class:

public class DeviceListConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(List<Device>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);
        return obj.Properties()
                  .Select(jp => new Device
                  {
                      Id = jp.Name,
                      Events = jp.Value["events"].ToObject<List<Event>>(serializer)
                  })
                  .ToList();
    }

    public override bool CanWrite
    {
        get { return false; }
    }

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

Now you can deserialize as you normally would and it should work as you want:

var root = JsonConvert.DeserializeObject<Root>(json);

Here is a working demo: https://dotnetfiddle.net/qORiPG

Related