You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于元素值将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}");
    }
}

动态适配新增子类

后续新增子类时,仅需两步:

  1. 创建新的Test子类,添加自定义字段
  2. 在TestConverter的_typeMapping字典中添加type字段值与新子类的映射关系

无需修改其他反序列化逻辑,新增字段会自动被填充。

内容的提问来源于stack exchange,提问作者freddyfearful

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 14:45:37