JSON 反序列化为 C# 类层次结构

问题描述

我从 web hook 中获取 JSON,其中对象的类型被烘焙到 JSON 本身中,有点像这样:

类型为“a”的数据示例:

{
    "type": "a","name": "object name","a_data": "something specific to type a"
}

以及类型为“b”的数据示例:

{
    "type": "b","name": "another object name","b_data": {
        "b_field1": "Something specific to type b","b_field2": "Something else specific to type b"
    }

我保证每个负载都具有 typename 属性type "a" 的所有负载都将另外有一个 a_data 属性type b 的所有负载将另外有一个 b_data 属性,它本身由属性 {{ 1}} 和 b_field1

(当然,为了说明这个问题,这非常简单——实际用例有更长、更复杂的 json 和更多类型)

我想反序列化为 C# 类,建模一个基类(至少包含 b_field2 属性,可能还有 name 属性)和两个派生类,一个用于序列化的对象在 type "a" json 中,还有一个用于在 type "b" json 中序列化的对象。

我想像这样定义 C# 类:

type

鉴于我不知道有效负载何时传入它是什么类型,我需要根据传入类型有条件地反序列化。但在我反序列化之前,我不“知道”那是什么类型。我很确定以下方法可行,但这不是一个特别好的解决方案,因为这意味着我需要反序列化两次:

    public class Base
    {
        public string type { get; set; }
        public string name { get; set; }
    }

    public class DeriviedA: Base
    {
        public string a_data { get; set; }
    }

    public class DervivedB : Base
    {
        public class DataB
        {
            public string b_field1 { get; set; }
            public string b_field2 { get; set; }
        }

        public DataB b_data { get; set; }
    }

现在除了需要进行不优雅的双重反序列化之外,请注意我无法将 public static Base Deserialize(string incomingJson) { Base tmp = JsonConvert.DeserializeObject<Base>(incomingJson); switch (tmp.type) { case "a": return JsonConvert.DeserializeObject<DerivedA>(incomingJson); case "b": return JsonConvert.DeserializeObject<DerivedB>(incomingJson); default: throw new InvalidOperationException($"unable to deserialize payload of type {tmp.type}"); } } 标记Base,即使我从不想要 {{1 }} 类本身。

无论如何,我对这样做的想法、策略或最佳实践很好奇。我的偏好是使用 Newtonsoft 进行反序列化,但这不是要求

解决方法

在使用序列化器之前,只需使用非反序列化 JSON 读取器来嗅探类型。

Newtonsoft 有一个非常简单的方法来做到这一点:

dynamic message = JObject.Parse(json);
string type = message.type;

https://www.newtonsoft.com/json/help/html/QueryJsonDynamic.htm