There are a couple of approaches you could take to handle this. The first (and simplest) approach is to use nested dictionaries in your TransactionChangeLog class:
public class TransactionChangeLog
{
[JsonProperty(PropertyName = "transaction_id")]
public int TransactionId { get; set; }
[JsonProperty(PropertyName = "status")]
public TransactionStatus Status { get; set; }
[JsonProperty(PropertyName = "changelog")]
public Dictionary<DateTime, Dictionary<string, TransactionChange>> Changelog { get; set; }
}
public class TransactionChange
{
public string From { get; set; }
public string To { get; set; }
}
You can then deserialize and dump out the data like this:
TransactionChangeLog changeLog = JsonConvert.DeserializeObject<TransactionChangeLog>(json);
Console.WriteLine("TransactionId: " + changeLog.TransactionId);
Console.WriteLine("TransactionStatus: " + changeLog.Status);
foreach (var dateKvp in changeLog.Changelog)
{
Console.WriteLine(dateKvp.Key); // change date
foreach (var fieldKvp in dateKvp.Value)
{
Console.WriteLine(" changed " + fieldKvp.Key + " from '" + fieldKvp.Value.From + "' to '" + fieldKvp.Value.To + "'");
}
}
Fiddle: https://dotnetfiddle.net/vXNcKi
Although the above will work, it's a little bit awkward to work with the nested dictionaries. Another approach is to use a JsonConverter to handle the deserialization of the varying JSON. This will allow you to use the classes as you defined them in your question. Here is how you might write the converter:
public class ChangeLogConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ICollection<TransactionChange>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
List<TransactionChange> changes = new List<TransactionChange>();
JObject changelog = JObject.Load(reader);
foreach (JProperty dateProp in changelog.Children<JProperty>())
{
DateTime changeDate = DateTime.ParseExact(dateProp.Name, "yyyy-MMM-dd hh:mm tt", CultureInfo.InvariantCulture);
foreach (JProperty fieldProp in dateProp.Value.Children<JProperty>())
{
TransactionChange change = new TransactionChange();
change.ChangeDate = changeDate;
change.Field = fieldProp.Name;
change.From = (string)fieldProp.Value["from"];
change.To = (string)fieldProp.Value["to"];
changes.Add(change);
}
}
return changes;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, add a [JsonConverter] attribute to the Changelog property in your TransactionChangeLog class:
public class TransactionChangeLog
{
[JsonProperty(PropertyName = "transaction_id")]
public int TransactionId { get; set; }
[JsonProperty(PropertyName = "status")]
public TransactionStatus Status { get; set; }
[JsonProperty(PropertyName = "changelog")]
[JsonConverter(typeof(ChangeLogConverter))]
public ICollection<TransactionChange> Changelog { get; set; }
}
You can then deserialize and dump out the data as you normally would:
TransactionChangeLog changeLog = JsonConvert.DeserializeObject<TransactionChangeLog>(json);
Console.WriteLine("TransactionId: " + changeLog.TransactionId);
Console.WriteLine("TransactionStatus: " + changeLog.Status);
foreach (TransactionChange change in changeLog.Changelog)
{
Console.WriteLine(change.ChangeDate + " - changed " + change.Field + " from '" + change.From + "' to '" + change.To + "'");
}
Fiddle: https://dotnetfiddle.net/1d3pUa