#c# #json #serialization #json.net
#c# #json #сериализация #json.net
Вопрос:
Я интегрируюсь с третьей стороной, и JSON, который они возвращают, запускает меня в цикл. Я хочу десериализовать его в класс. но большинство имен свойств могут меняться.
{
"transaction_id":1,
"status":"Reviewing",
"changelog":{
"2016-Mar-15 10:28 AM":{
"status":{
"from":"Approved",
"to":"Reviewing"
},
"total":{
"from":123.45,
"to":246.90
},
"shipping_address_1":{
"from":"321 S Main St.",
"to":"8355 NW 74th St"
},
"shipping_city":{
"from":"New York",
"to":"Medley"
},
"shipping_state":{
"from":"NY",
"to":"FL"
},
"shipping_postal":{
"from":"10002",
"to":"33166"
}
}
}
}
Я хотел бы иметь класс, похожий на этот.
public class TransactionChangeLog
{
[JsonProperty(PropertyName = "transaction_id")]
public int TransactionId { get; set; }
[JsonProperty(PropertyName = "status")]
public TransactionStatus Status { get; set; }
[JsonProperty(PropertyName = "changelog")]
public ICollection<TransactionChange> Changelog { get; set; }
}
public class TransactionChange
{
// ?? What to do with this.
public DateTime ChangeDate { get; set; }
// ?? What to do with this.
public string Field { get; set; }
public string From { get; set; }
public string To { get; set; }
}
Ответ №1:
Есть несколько подходов, которые вы могли бы предпринять, чтобы справиться с этим. Первый (и самый простой) подход заключается в использовании вложенных словарей в вашем TransactionChangeLog
классе:
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; }
}
Затем вы можете десериализовать и выгрузить данные следующим образом:
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 "'");
}
}
Скрипка:https://dotnetfiddle.net/vXNcKi
Хотя вышеуказанное будет работать, немного неудобно работать с вложенными словарями. Другой подход заключается в использовании JsonConverter
для обработки десериализации изменяющегося JSON. Это позволит вам использовать классы так, как вы определили их в своем вопросе. Вот как вы могли бы написать конвертер:
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();
}
}
Чтобы использовать конвертер, добавьте [JsonConverter]
атрибут к Changelog
свойству в вашем TransactionChangeLog
классе:
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; }
}
Затем вы можете десериализовать и выгрузить данные, как обычно:
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 "'");
}
Скрипка:https://dotnetfiddle.net/1d3pUa
Комментарии:
1. Брайан. Спасибо за подробный пример. это было то, что я искал.
2. Нет проблем; рад, что смог помочь.
Ответ №2:
Если я вас правильно понимаю, содержимое changelog
может меняться, и вы не знаете возможных имен свойств во время компиляции.
Если это так, вам нужно десериализовать что-то, что не является статически типизированным, например JObject
, которое может быть использовано для последующего запроса данных в стиле LINQ.