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

无类型鉴别器时System.Text.Json反序列化嵌套多态对象的问题

问题描述

使用System.Text.Json实现多态类型(抽象基类+派生类)序列化时,框架会自动生成$type类型鉴别器字段用于反序列化。但在Minimal API的Post端点接收客户端请求时,客户端提交的JSON仅包含自定义的枚举标识字段(如TypeAModels、TypeBModels),不含$type,此时直接反序列化抽象基类会抛出System.NotSupportedException异常。

优雅解决方案:自定义JsonConverter

通过实现JsonConverter,利用自定义的枚举字段作为类型鉴别依据,替代框架默认的$type字段,实现自动反序列化。

1. 实现TypeA的自定义转换器

public class TypeAConverter : JsonConverter<TypeA>
{
    public override TypeA? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // 先将JSON读入JsonDocument,方便读取枚举字段
        using var doc = JsonDocument.ParseValue(ref reader);
        var root = doc.RootElement;
        
        // 获取TypeAModels枚举值
        if (!root.TryGetProperty(nameof(TypeAModels), out var typeProp) || !typeProp.TryGetInt32(out var typeValue))
        {
            throw new JsonException("TypeAModels字段缺失或无效");
        }
        
        // 根据枚举值选择具体类型反序列化
        return (TypeAModels)typeValue switch
        {
            TypeAModels.A => JsonSerializer.Deserialize<TypeA_A>(root.GetRawText(), options),
            TypeAModels.B => JsonSerializer.Deserialize<TypeA_B>(root.GetRawText(), options),
            _ => throw new JsonException($"不支持的TypeAModels值:{typeValue}")
        };
    }

    public override void Write(Utf8JsonWriter writer, TypeA value, JsonSerializerOptions options)
    {
        // 序列化时直接用默认逻辑,也可自定义输出格式
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
}

2. 实现TypeB的自定义转换器

public class TypeBConverter : JsonConverter<TypeB>
{
    public override TypeB? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var doc = JsonDocument.ParseValue(ref reader);
        var root = doc.RootElement;
        
        if (!root.TryGetProperty(nameof(TypeBModels), out var typeProp) || !typeProp.TryGetInt32(out var typeValue))
        {
            throw new JsonException("TypeBModels字段缺失或无效");
        }
        
        return (TypeBModels)typeValue switch
        {
            TypeBModels.A => JsonSerializer.Deserialize<TypeB_A>(root.GetRawText(), options),
            TypeBModels.B => JsonSerializer.Deserialize<TypeB_B>(root.GetRawText(), options),
            _ => throw new JsonException($"不支持的TypeBModels值:{typeValue}")
        };
    }

    public override void Write(Utf8JsonWriter writer, TypeB value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
}

3. 在Minimal API中配置转换器

在Program.cs中注册自定义转换器到Json序列化选项:

var builder = WebApplication.CreateBuilder(args);

// 配置Json序列化选项,添加自定义转换器
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.Converters.Add(new TypeAConverter());
    options.SerializerOptions.Converters.Add(new TypeBConverter());
});

var app = builder.Build();

// 定义Post端点
app.MapPost("/test", (TypeA typeA) =>
{
    // 处理请求逻辑
    return Results.Ok(typeA);
});

app.Run();

4. 验证效果

客户端提交如下JSON:

{
  "Age": 30,
  "TypeB": {
    "Year": 1982,
    "TypeBModels": 1
   },
  "TypeAModels": 1
}

此时Minimal API会自动将其反序列化为TypeA_A类型,嵌套的TypeB也会被正确反序列化为TypeB_A,无需手动解析JSON字段。

额外优化

  • 可将转换器中的重复逻辑(如读取枚举字段)封装为通用方法,减少代码冗余
  • 若枚举字段为字符串类型,只需调整为TryGetString方法即可适配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:50:56