在.NET控制器中如何让FromBody参数支持类继承以正确解析子类?
解决方案
首先明确问题根因:ASP.NET Core 默认的JSON序列化器仅会按属性声明的BaseSchema类型进行字段解析,不会自动识别子类类型,因此子类独有的属性会被丢弃。下面提供3种可落地的实现方案:
方案1:使用System.Text.Json原生多态支持(.NET 7+ 推荐)
.NET 7及以上版本的System.Text.Json原生支持多态序列化/反序列化,只需给父类添加特性标注即可:
// 引入命名空间 using System.Text.Json.Serialization; [JsonPolymorphic(TypeDiscriminatorPropertyName = "SchemaType")] [JsonDerivedType(typeof(BaseSchema), typeDiscriminator: "Base")] [JsonDerivedType(typeof(SingleLineSchema), typeDiscriminator: "SingleLine")] public class BaseSchema { public DataType Type { set; get; } public string Label { set; get; } public bool Visibility { set; get; } public string Hint { set; get; } }
请求传参时在JSON中添加SchemaType判别字段即可:
{ "Id": "xxx", "FormSchema": { "SchemaType": "SingleLine", "Type": 1, "Label": "单行文本", "Visibility": true, "Hint": "请输入内容", "CustomFieldExample": "自定义属性值" } }
反序列化后会自动生成对应的子类实例,不会丢失属性。
方案2:自定义JsonConverter(兼容所有.NET版本)
如果是.NET 6及以下版本,或者不想新增判别字段,可以自定义JSON转换器,根据已有字段(比如你已有的DataType枚举值)判断要实例化的子类:
public class BaseSchemaConverter : JsonConverter<BaseSchema> { public override BaseSchema? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 先加载JsonElement判断DataType值 using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; var dataType = root.GetProperty("Type").GetInt32(); // 按你的DataType枚举实际值调整 return dataType switch { // 假设DataType.SingleLine对应单行文本子类 (int)DataType.SingleLine => JsonSerializer.Deserialize<SingleLineSchema>(root.GetRawText(), options), // 其他子类对应分支 _ => JsonSerializer.Deserialize<BaseSchema>(root.GetRawText(), options) }; } public override void Write(Utf8JsonWriter writer, BaseSchema value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, value.GetType(), options); } }
然后在Program.cs中注册转换器:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new BaseSchemaConverter()); });
方案3:直接接收JSON对象手动转换
如果不想改序列化配置,可以将DTO中的属性类型改为JsonElement(System.Text.Json)或JObject(Newtonsoft.Json),业务层手动转成对应子类:
public class UpdateFormSchemaRequestDto { public string Id { set; get; } public JsonElement FormSchema { set; get; } }
业务层转换代码:
public async Task<Response> UpdateFormSchema(UpdateFormSchemaRequestDto dto) { BaseSchema schema; var dataType = dto.FormSchema.GetProperty("Type").GetInt32(); if (dataType == (int)DataType.SingleLine) { schema = dto.FormSchema.Deserialize<SingleLineSchema>(); } else { schema = dto.FormSchema.Deserialize<BaseSchema>(); } // 后续业务逻辑 }
注意事项
- 如果使用Newtonsoft.Json的
TypeNameHandling配置实现多态,不要对外网公开接口,避免恶意JSON payload触发反序列化注入漏洞。
内容的提问来源于stack exchange,提问作者Natheeshkumar Rangasamy
相关产品推荐
相关产品推荐

