【问题标题】:How to de-serialize JSON array containing objects of different class types that have same parent class如何反序列化包含具有相同父类的不同类类型对象的 JSON 数组
【发布时间】:2020-08-31 03:04:43
【问题描述】:

我有两个具有相同父类的类,如下所示:

public class Parent
{
    public string Name { get; set; } = string.Empty;
    public string Type { get; set; } = string.Empty;
}

public class ChildInteger : Parent
{
    public int Value { get; set; } = 0;
}

public class ChildDateTime : Parent
{
    public DateTime Value { get; set; } = DateTime.MinValue;
}

现在我有一个 API,它返回一个 JSON 字符串类型的数组,其中包含 ChildInteger、ChildDateTime 类型的对象。

JSON 字符串:

[{"Value":1,"Name":"Child Integer 1","Type":"Integer"},{"Value":"2020-08-31T08:29:11.9002559+05:30","Name":"Child DateTime 1","Type":"DateTime"}]

如何反序列化回正确的类型,使值属性不丢失?

  1. 由于 value 属性丢失,以下方法将不起作用。

    列表 parentList1 = new List(); parentList1 = JsonConvert.DeserializeObject(json);

注意:这些类来自第三方 API。所以需要在不修改类的情况下实现。

【问题讨论】:

    标签: c# json-deserialization


    【解决方案1】:

    为什么不试试这样的:

    public class Parent
    {
        public string Name { get; set; } = string.Empty;
        public string Type { get; set; } = string.Empty;
    }
    
    public class ChildInteger : Parent
    {
        public int Value { get; set; } = 0;
    }
    
    public class ChildDateTime : Parent
    {
        public DateTime Value { get; set; } = DateTime.MinValue;
    }
        
    public static void Main()
    {
            var json = "[{\"Value\":1,\"Name\":\"Child Integer 1\",\"Type\":\"Integer\"},{\"Value\":\"2020-08-31T08:29:11.9002559+05:30\",\"Name\":\"Child DateTime 1\",\"Type\":\"DateTime\"}]";
            List<Parent> jarray = JArray.Parse(json).AsEnumerable().Select<JToken, Parent>(x => {
            switch(x["Type"].ToObject<string>()) 
            {
                case "Integer":
                    return x.ToObject<ChildInteger>();
                case "DateTime":
                    return x.ToObject<ChildDateTime>();
            }
                return null;
            }).ToList();
            
       Console.WriteLine(jarray.Count()); // prints 2
       foreach(var p in jarray) {
          Console.WriteLine(p.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public).GetValue(p));
       }
    // prints
    // 1
    // 08/31/2020 02:59:11
    }
    

    注意:我必须为列表中缺少的子类型添加继承,并将ChildDateTimeDate 属性更改为Value

    【讨论】:

    • 感谢您指出有问题的错误,看来此解决方案有效且看起来不错。
    【解决方案2】:

    对此有很多解决方案。假设您不想要显式转换器,并且想要离散类型,不想使用对象或动态,那么您可以使用JsonSubTypes。需要注意的是,这将更适用于更复杂的模式......但是,添加胡椒和盐来调味

    给定

    [JsonConverter(typeof(JsonSubtypes), "type")]
    [JsonSubtypes.KnownSubType(typeof(ChildInteger), "Integer")]
    [JsonSubtypes.KnownSubType(typeof(ChildDateTime), "DateTime")]
    public abstract class Child
    {
       [JsonProperty("type")]
       public virtual string Type { get; }
    
       public string Name { get; set; }
    
       public abstract string GetValue();
    }
    
    public class ChildInteger : Child
    {
       public override string Type => "Integer";
       public int Value { get; set; }
    
       public override string GetValue()
          => Value.ToString();
    }
    
    public class ChildDateTime : Child
    {
       public override string Type => "DateTime";
       public DateTime Value { get; set; }
    
       public override string GetValue()
          => Value.ToString();
    }
    

    用法

    var input = "[{\"Value\":1,\"Name\":\"Child Integer 1\",\"Type\":\"Integer\"},{\"Value\":\"2020-08-31T08:29:11.9002559+05:30\",\"Name\":\"Child DateTime 1\",\"Type\":\"DateTime\"}]";
    var results = JsonConvert.DeserializeObject<List<Child>>(input);
    
    foreach (var item in results)
       Console.WriteLine($"{item.Name}, {item.GetValue()}");
    

    输出

    Child Integer 1, 1 
    Child DateTime 1, 08/31/2020 02:59:11
    

    Full Demo here

    【讨论】:

    • 该类来自 API,我无法修改该类。可能我应该提到我需要在不修改类的情况下实现它。无论如何感谢您的回复。在处理 XML 和 BSON 时,我也使用 typeof 关键字。在这里我想在不修改类的情况下这样做,因为 API 属于第三方。
    【解决方案3】:

    如果您的目标只是反序列化具有不同值类型的 JSON,您可能更喜欢使用 dynamic 属性。 参考: Using type dynamic.

    public class Parent
    {
        public string Name { get; set; } = string.Empty;
        public string Type { get; set; } = string.Empty;
        public dynamic Value { get; set; }
    }
    

    正常反序列化var result = JsonConvert.DeserializeObject&lt;List&lt;Parent&gt;&gt;(json);

    【讨论】:

    • 该类来自 API,我无法修改该类。可能我应该提到我需要在不修改类的情况下实现它。无论如何感谢您的回复。
    猜你喜欢
    • 1970-01-01
    • 2016-12-16
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 2015-04-15
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    相关资源
    最近更新 更多