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

如何基于枚举类型反序列化含抽象类的复杂嵌套JSON?

基于枚举类型信息反序列化嵌套JSON到抽象类派生对象

问题背景

我需要将一段复杂的嵌套JSON反序列化为自定义对象,其中涉及抽象类型,必须根据JSON中枚举存储的类型信息,匹配对应的派生类完成实例化。以下示例仅展示一层嵌套,实际场景中嵌套层级更多,包含的对象与类型也更复杂。

JSON示例

{
   "screen":{
      "type":"Component",
      "footer":{
         "type":"Bar"
      },
      "header":{
         "type":"Top"
      }
   }
}

对应C#类定义

public abstract class Screen
{
    public abstract ScreenType Type { get; }
}

public enum ScreenType
{
    Component,
    b,
    c,
    d,
    e
}

public sealed class ComponentScreen : Screen
{
    public override ScreenType Type => ScreenType.Component;
    public Header? Header { get; init; }
    public Footer? Footer { get; init; }
    public bool? ShowStuff { get; init; }
}

public interface ITyped<TEnum> where TEnum : Enum
{
    TEnum Type { get; }
}

public abstract class Header : ITyped<HeaderType>
{
    public abstract HeaderType Type { get; }
}

public enum HeaderType
{
    Top,
    b,
    c,
    d
}

public sealed class TopScreenHeader : Header
{
    public override HeaderType Type => HeaderType.Top;
    public string MyStuff { get; }
}

当前尝试的代码及错误

我原本使用Newtonsoft.Json的TypeNameHandling来处理,但运行时报错:

var screen = JsonConvert.DeserializeObject<Screen>(jsonString, new JsonSerializerSettings {
    TypeNameHandling = TypeNameHandling.Objects,
    ContractResolver = new CamelCasePropertyNamesContractResolver()
});

错误信息:

Could not create an instance of type Screens.Screen. Type is an interface or abstract class and cannot be instantiated. Path 'screen', line 1, position 10.

这是因为TypeNameHandling依赖JSON中的$type字段识别类型,而我们的JSON用的是自定义type枚举字段,无法自动匹配派生类。

解决方案

我们可以自定义通用枚举类型转换器,通过读取JSON中的type枚举值,映射到对应的派生类完成反序列化,既不用修改抽象类,也能避免重复编写大量转换器。

1. 通用枚举类型转换器

先实现一个通用转换器基类,复用核心逻辑:

public class TypedEnumConverter<TBase, TEnum> : JsonConverter<TBase> 
    where TBase : class, ITyped<TEnum> 
    where TEnum : Enum
{
    private readonly Dictionary<TEnum, Type> _typeMap;

    public TypedEnumConverter(Dictionary<TEnum, Type> typeMap)
    {
        _typeMap = typeMap ?? throw new ArgumentNullException(nameof(typeMap));
    }

    public override TBase ReadJson(JsonReader reader, Type objectType, TBase existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader);
        var typeEnum = jObject["type"].ToObject<TEnum>();
        
        if (!_typeMap.TryGetValue(typeEnum, out var targetType))
            throw new JsonSerializationException($"未知{typeof(TEnum).Name}类型: {typeEnum}");
        
        return (TBase)jObject.ToObject(targetType, serializer);
    }

    public override void WriteJson(JsonWriter writer, TBase value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

2. 配置类型映射与反序列化

针对每个抽象类,定义枚举到派生类的映射,然后配置转换器进行反序列化:

// 定义Screen的类型映射
var screenTypeMap = new Dictionary<ScreenType, Type>
{
    { ScreenType.Component, typeof(ComponentScreen) },
    // 其他ScreenType对应的派生类
};

// 定义Header的类型映射
var headerTypeMap = new Dictionary<HeaderType, Type>
{
    { HeaderType.Top, typeof(TopScreenHeader) },
    // 其他HeaderType对应的派生类
};

// 配置序列化设置
var settings = new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    Converters = new List<JsonConverter>
    {
        new TypedEnumConverter<Screen, ScreenType>(screenTypeMap),
        new TypedEnumConverter<Header, HeaderType>(headerTypeMap)
        // 其他抽象类的转换器
    }
};

// 方式一:通过根对象类反序列化
public class RootObject
{
    public Screen Screen { get; set; }
}
var root = JsonConvert.DeserializeObject<RootObject>(jsonString, settings);
var screen = root.Screen;

// 方式二:直接提取screen节点反序列化
var jRoot = JObject.Parse(jsonString);
var screenJson = jRoot["screen"].ToString();
var screen = JsonConvert.DeserializeObject<Screen>(screenJson, settings);

说明

  • 通用转换器TypedEnumConverter可复用在所有实现ITyped<TEnum>接口的抽象类上,只需传入对应的类型映射字典。
  • 转换器自动读取JSON中的type字段,解析为枚举值后匹配到对应派生类,完成反序列化。
  • 新增抽象类(如Footer)时,只需补充对应的类型映射和转换器实例,无需重复编写反序列化逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 14:40:57