Converting JSON Objects into C# List<>
The problem I have is basically to fix some weird data structure coming from a data provider, which I'm sure there's some background story on why the structure is that way, into a reasonable structure that I can later on map into a model or entity more easily.
Let's start from the begining and go through some really basic examples when it comes to data binding. Imagine we have a JSON object which you're going to use as the body of a HTTP POST request.
{
"status": true,
"version": "2.0.3",
"expires": "1551829009.0"
}
In the C# side, the parameter of the API can be as such and everything will be bound perfectly.
public class DataRequest
{
public bool Status {get;set;}
public string Version {get;set;}
public TimeSpan Expires {get;set;}
}
What if now, we have a new field in the JSON object that doesn't really follow your naming convention, like the status_code
below:
{
"status": true,
"version": "2.0.3",
"status_code": 200,
"expires": "1551829009.0"
}
That's still not a big deal and with the JsonProperty
attribute you can customize which field to bind to.
public class DataRequest
{
public bool Status {get;set;}
public string Version {get;set;}
[JsonProperty("status_code")]
public int StatusCode {get;set;}
public TimeSpan Expires {get;set;}
}
The problem gets harder if the field name coming from the JSON object is dynamic. In my example, I have a scructure that's essentially an array, but it wasn't built this way and I have no access to change that so I have to work around it.
{
"status": true,
"version": "2.0.3",
"status_code": 200,
"expires": "1551829009.0",
"players":
{
"a_hales": {
"seasonal_role": "batsman",
"fullname": "Alex Hales",
"name": "AD Hales"
},
"j_root": {
"seasonal_role": "batsman",
"fullname": "Joe Root",
"name": "JE Root"
},
"d_bishoo": {
"seasonal_role": "bowler",
"fullname": "Devendra Bishoo",
"name": "Devendra Bishoo"
}
}
}
Using JsonProperty
is out of the equation because I have no idea what the property name for each individual player is going to be, also at the end of the day I want to bind that property name to the value of an Id
field. Another tricky point is the fact that's players
is an object and I want it to be an array.
Here comes the solution
I want each individual player inside that players
object to be bound to this C# class.
public class Player
{
public string Id { get; set; }
[JsonProperty("seasonal_role")]
public string SeasonalRole { get; set; }
public string Fullname { get; set; }
public string Name { get; set; }
}
The DataRequest
would look something along these lines, but the Players
property wouldn't be bound as it expects a JSON array instead of an object.
public class DataRequest
{
public bool Status {get;set;}
public string Version {get;set;}
[JsonProperty("status_code")]
public int StatusCode {get;set;}
public TimeSpan Expires {get;set;}
public List<Player> Player {get;set;}
}
There it comes a custom JsonConverter
from Newtonsoft for the win.
public class PlayersConverter : JsonConverter
{
// This is used when you're converting the C# List back to a JSON format
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartArray();
foreach (var player in (List<Player>) value)
{
writer.WriteRawValue(JsonConvert.SerializeObject(player));
}
writer.WriteEndArray();
}
// This is when you're reading the JSON object and converting it to C#
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var response = new List<Player>();
// Loading the JSON object
JObject players = JObject.Load(reader);
// Looping through all the properties. C# treats it as key value pair
foreach (var player in players)
{
// Finally I'm deserializing the value into an actual Player object
var p = JsonConvert.DeserializeObject<Player>(player.Value.ToString());
// Also using the key as the player Id
p.Id = player.Key;
response.Add(p);
}
return response;
}
public override bool CanConvert(Type objectType) => objectType == typeof(List<Player>);
}
And to close the loop, we need to update the DataRequest
to use this new converter.
public class DataRequest
{
public bool Status {get;set;}
public string Version {get;set;}
[JsonProperty("status_code")]
public int StatusCode {get;set;}
public TimeSpan Expires {get;set;}
[JsonConverter(typeof(PlayersConverter))]
public List<Player> Player {get;set;}
}
I hope it helps.
Cheers