如何在C#中通过Newtonsoft根据Type属性反序列化动态Data节点
基于Newtonsoft.Json根据Type属性反序列化多态Data节点的解决方案
问题背景
开发C#应用时,API返回的JSON中Block节点下的Data结构多变,仅共用Type属性,需要根据Type将Data反序列化为对应的具体类,以兼容所有数据类型并能通过Type判断对象类型。
API返回的JSON示例:
{ "nextLink": "NextLink", "value": [ { "Block": { "BlockName": "Name", "BlockClass": "Class", "IsVisibile": true, "Data": { "Type": "Footer", "Values": [ { "Property1": "Property1", "Property2": "Property2" } ], "Show": false } } }, { "Block": { "BlockName": "Name", "BlockClass": "Class", "IsVisibile": true, "Data": { "Type": "Header", "Title": "Main title", "Subtitle": "Subtitle" } } }, { "Block": { "BlockName": "Name", "BlockClass": "Class", "IsVisibile": true, "Data": { "Type": "Body", "Information": "Info", "AdditionalText": "More text", "Projects": [ "Project1", "Project2" ] } } } ] }
现有类定义(需修改):
public class Blocks { public string NextLink { get; set; } public Response[] Value { get; set; } } public class Response { public Block Block { get; set; } } public class Block { public string BlockName { get; set; } public string BlockClass { get; set; } public bool IsVisible { get; set; } public Data Data { get; set; } } public class Data { public string Type { get; set; } } public class FooterType { public bool Show { get; set; } public object[] Values { get; set; } } public class HeaderType { public string Title { get; set; } public string Subtitle { get; set; } } public class BodyType { public string Information { get; set; } public string AdditionalText { get; set; } public object[] Projects { get; set; } }
解决方案
方案一:使用Newtonsoft.Json.Subtypes扩展包(推荐)
该方案通过特性配置多态映射,代码简洁易维护。
- 安装扩展包
通过NuGet安装Newtonsoft.Json.Subtypes:
Install-Package Newtonsoft.Json.Subtypes
- 修改类结构
将Data改为抽象基类,并通过特性指定Type与子类的映射关系;所有数据子类继承Data:
// 配置多态映射,基于Type属性匹配子类 [JsonConverter(typeof(JsonSubtypes), nameof(Type))] [JsonSubtypes.KnownSubType(typeof(FooterType), "Footer")] [JsonSubtypes.KnownSubType(typeof(HeaderType), "Header")] [JsonSubtypes.KnownSubType(typeof(BodyType), "Body")] public abstract class Data { public string Type { get; set; } } // 子类继承Data基类 public class FooterType : Data { public bool Show { get; set; } public object[] Values { get; set; } } public class HeaderType : Data { public string Title { get; set; } public string Subtitle { get; set; } } public class BodyType : Data { public string Information { get; set; } public string AdditionalText { get; set; } public object[] Projects { get; set; } }
- 反序列化与类型判断
直接使用默认反序列化方法,之后可通过Type属性或类型转换判断具体对象:
// 反序列化API响应 string apiJson = // 从API获取的JSON字符串 var blocks = JsonConvert.DeserializeObject<Blocks>(apiJson); // 遍历并处理不同类型的Data foreach (var response in blocks.Value) { var data = response.Block.Data; switch (data.Type) { case "Footer": var footerData = data as FooterType; // 使用footerData的Show、Values等属性 break; case "Header": var headerData = data as HeaderType; // 使用headerData的Title、Subtitle等属性 break; case "Body": var bodyData = data as BodyType; // 使用bodyData的Information、Projects等属性 break; default: // 处理未知类型 break; } }
方案二:自定义Json转换器(无额外依赖)
若不想引入扩展包,可自定义转换器实现多态反序列化。
- 编写自定义转换器
创建继承自JsonConverter<Data>的转换器类,在ReadJson方法中根据Type值选择对应子类:
public class DataConverter : JsonConverter<Data> { public override Data ReadJson(JsonReader reader, Type objectType, Data existingValue, bool hasExistingValue, JsonSerializer serializer) { // 加载JSON对象 var jObject = JObject.Load(reader); // 获取Type属性值 var type = jObject["Type"].Value<string>(); // 根据Type反序列化为对应子类 return type switch { "Footer" => jObject.ToObject<FooterType>(serializer), "Header" => jObject.ToObject<HeaderType>(serializer), "Body" => jObject.ToObject<BodyType>(serializer), _ => throw new NotSupportedException($"不支持的Data类型:{type}") }; } public override void WriteJson(JsonWriter writer, Data value, JsonSerializer serializer) { // 序列化时直接输出对象内容 serializer.Serialize(writer, value); } }
- 配置转换器
有两种方式启用转换器:
- 方式一:在
Data类上添加特性:
[JsonConverter(typeof(DataConverter))] public abstract class Data { public string Type { get; set; } }
- 方式二:反序列化时传入配置:
var settings = new JsonSerializerSettings(); settings.Converters.Add(new DataConverter()); var blocks = JsonConvert.DeserializeObject<Blocks>(apiJson, settings);
- 类型判断与使用
与方案一一致,通过Type属性或类型转换处理不同子类对象。
内容的提问来源于stack exchange,提问作者PureJoyCoder
相关产品推荐
相关产品推荐

