【问题标题】:C# Deserializing JSON to class dependent on a type propertyC# 将 JSON 反序列化为依赖于类型属性的类
【发布时间】:2020-04-19 18:03:43
【问题描述】:

假设我有这个小 json sn-p:

{
  "Type": "Bar",
  "BarOnly": "This is a string readable when deserialized to the Bar class only, as declared in my type key"
}

我也有这三个类:

public class Base
{
    public enum SampleEnum
    {
        Bar,
        Baz,
    }

    public SampleEnum Type
    {
        get;
        set;
    }
}

public class Bar : Base
{
    public string BarOnly
    {
        get;
        set;
    }
}

public class Baz : Base
{
    public string BazOnly
    {
        get;
        set;
    }
}

基于 json sn-p 中的 Type 属性,我希望将其反序列化为 Bar 或 Baz。
我的第一个想法是首先将其反序列化为 Base 类,然后使用其类型和 switch 语句将 JSON 再次反序列化为相应的类。 (使用Newtonsoft.Json

var type = JsonConvert.DeserializeObject<Base>(json).Type;
string message = "";
switch (type)
{
    case (Base.SampleEnum.Bar):
        message = JsonConvert.DeserializeObject<Bar>(json).BarOnly;
        break;
    case (Base.SampleEnum.Baz):
        message = JsonConvert.DeserializeObject<Baz>(json).BazOnly;
        break;
}
Console.WriteLine(message);

毋庸置疑,这个过程极其多余、乏味,而且由于 switch 语句是硬编码的,因此根本不是很“动态”。
另一个想法是使用泛型类作为基类,并传入它应该反序列化到的实际类的类型,但我最终会使用相同的 switch 语句来确定该类应该是什么。
由于您无法将枚举映射到类类型,因此我还考虑使用 Dictionary 将可能的枚举值映射到其对应的类;不过,这仍然会使映射过程硬编码。
有什么方法可以根据 json 对象的 type 属性动态获取要反序列化的相应类?

编辑:对于应该如何使用它以及如何获取数据似乎有些混乱;让我提供一些背景信息。
我正在遍历一个包含许多不同电子表格文件的目录,主要是 CSV 和 XML 文件。这些提要中的每一个都有一个“元文件”,描述如何处理它们的内容。这包括校验和、分隔符和其他信息。他们还声明其父文件的类型(CSV、XML 等)。因此,它们共享许多公共属性(例如我的示例中的 Base 类),但也有自己的一组属性。它们派生自一个抽象类,该类要求它们实现一个函数,该函数返回相应的提要处理类的实例,并直接使用元类中的值进行初始化。我希望这是有道理的。

【问题讨论】:

  • 使用反射找到对应的类,然后使用反射调用方法进行反序列化。
  • 一般情况下Newtonsoft Json可以将$type信息放入json内容中。这是Type 财产吗?
  • @LasseV.Karlsen 当然,反思始终是一个答案;)不过,如果有其他方法,我不想弄脏它。
  • 为什么不把抽象方法放到基类中,让扩展类实现它,比如公共抽象字符串GetMessage();
  • 那么,Bar 可以返回 BarOnly,Baz 可以返回 BazOnly,而您对具体类一无所知?没有枚举,没有类型检查,我猜是“动态”的东西 :)

标签: c# json class generics types


【解决方案1】:

@OguzOzgul 的评论是正确的。对于由需要序列化和反序列化的接口组成的对象,我已经无数次这样做了。

参见 Newtonsoft 的 TypeNameHandling: https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm

您的 json 文件看起来会略有不同:

{
    "$type": "SomeNamespace.Bar",
    "BarOnly": "This is a string readable when deserialized to the Bar class only, as declared in my type key"
}

如果你使用

new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
}

在序列化过程中,它将添加所有对象的完整类型名称,以确保 newtonsoft 在反序列化过程中知道它们的类型(假设您使用相同的设置)。这样您就不必编写自己的自定义代码来进行类型检测。

【讨论】:

  • 这是正确的答案,但也值得指出的是,TypeNameHandling.All 需要谨慎。它不应该用在 JSON 输入可以由人工制作的面向公众的 API 上。见alphabot.com/security/blog/2017/net/…
  • 哦,很高兴知道@NateBarbettini,感谢您提供的信息:) 回想起来,幸运的是我只将它用于私有 API
猜你喜欢
  • 2020-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多