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