如何基于元素值将JSON数组元素反序列化为对应子类?(C#)
C# Newtonsoft.Json 基于JSON字段动态反序列化为子类的解决方案
问题根源
你之前的实现大概率是先将JSON数组反序列化为抽象类Test的集合,再尝试强制转换为子类——但Test类中没有子类的自定义字段,转换后自然会出现子类字段为null的情况。要解决这个问题,需要让Newtonsoft.Json在反序列化时根据type字段动态选择目标子类,这就需要自定义JsonConverter来处理类型映射逻辑。
实现步骤
1. 定义抽象类与子类
保持你原有的类结构即可,确保每个子类都继承抽象类的type属性:
public abstract class Test { public string stringId { get; set; } public string type { get; set; } } public class TestPlus : Test { public int plusField { get; set; } } public class TestPlusPlus : Test { public string plusPlusField { get; set; } } public class TestPlusPlusPlus : Test { public bool triplePlusField { get; set; } }
2. 自定义类型转换转换器
创建继承自JsonConverter的转换器,核心逻辑是读取type字段,匹配对应的子类并完成反序列化:
public class TestConverter : JsonConverter { // 维护type字段值与子类的映射,新增子类时仅需在此添加条目 private static readonly Dictionary<string, Type> _typeMapping = new Dictionary<string, Type> { { "TestPlus", typeof(TestPlus) }, { "TestPlusPlus", typeof(TestPlusPlus) }, { "TestPlusPlusPlus", typeof(TestPlusPlusPlus) } }; public override bool CanConvert(Type objectType) { // 仅处理Test抽象类及其子类 return typeof(Test).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 先将JSON加载为JObject,方便读取type字段 var jObject = JObject.Load(reader); var typeName = jObject["type"]?.Value<string>(); // 校验type字段有效性 if (string.IsNullOrEmpty(typeName) || !_typeMapping.TryGetValue(typeName, out var targetType)) { throw new JsonSerializationException($"无法识别的类型标识:{typeName}"); } // 创建子类实例并填充所有字段 var targetInstance = Activator.CreateInstance(targetType); serializer.Populate(jObject.CreateReader(), targetInstance); return targetInstance; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 序列化逻辑默认即可,如需自定义可在此扩展 serializer.Serialize(writer, value); } }
3. 使用转换器完成反序列化
有两种方式启用转换器:
方式一:通过特性全局生效
在抽象类Test上添加特性,所有涉及Test的反序列化都会自动使用该转换器:
[JsonConverter(typeof(TestConverter))] public abstract class Test { public string stringId { get; set; } public string type { get; set; } } // 反序列化代码 var jsonContent = File.ReadAllText("你的JSON文件路径"); var testList = JsonConvert.DeserializeObject<List<Test>>(jsonContent); // 转换为字典(按stringId作为键) var testDict = testList.ToDictionary(item => item.stringId);
方式二:反序列化时传入转换器(临时生效)
如果不想修改类特性,可在反序列化时手动指定转换器:
var serializerSettings = new JsonSerializerSettings(); serializerSettings.Converters.Add(new TestConverter()); var jsonContent = File.ReadAllText("你的JSON文件路径"); var testList = JsonConvert.DeserializeObject<List<Test>>(jsonContent, serializerSettings); var testDict = testList.ToDictionary(item => item.stringId);
4. 验证子类字段
现在遍历字典时,子类的自定义字段会正常赋值:
foreach (var entry in testDict) { Console.WriteLine($"ID: {entry.Key}, Type: {entry.Value.type}"); if (entry.Value is TestPlus plus) { Console.WriteLine($"TestPlus字段值:{plus.plusField}"); } else if (entry.Value is TestPlusPlus plusPlus) { Console.WriteLine($"TestPlusPlus字段值:{plusPlus.plusPlusField}"); } else if (entry.Value is TestPlusPlusPlus triplePlus) { Console.WriteLine($"TestPlusPlusPlus字段值:{triplePlus.triplePlusField}"); } }
动态适配新增子类
后续新增子类时,仅需两步:
- 创建新的
Test子类,添加自定义字段 - 在
TestConverter的_typeMapping字典中添加type字段值与新子类的映射关系
无需修改其他反序列化逻辑,新增字段会自动被填充。
内容的提问来源于stack exchange,提问作者freddyfearful
相关产品推荐
相关产品推荐

