Json.Net反序列化和序列化具有更改名称的属性

本文关键字:属性 反序列化 Net 序列化 Json | 更新日期: 2023-09-27 18:14:06

我正在与第三方集成,他们返回的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; }
}

Json.Net反序列化和序列化具有更改名称的属性

您可以采用几种方法来处理此问题。第一种(也是最简单的)方法是在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();
    }
}

要使用转换器,在TransactionChangeLog类的Changelog属性中添加[JsonConverter]属性:

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

如果我理解正确的话,changelog的内容可能会有所不同,并且您在编译时不知道可能的属性名称。

如果是这种情况,您需要反序列化到非静态类型的东西,例如JObject,它可以用于之后对数据进行linq风格的查询。