ASP.NET Core Web API中如何为动态JSON构建合适的C#类模型
实现方案:多态模型 + 自定义JSON转换器
该方案完全匹配你的需求,既不会出现多余的null属性,也能通过枚举约束类型取值,完美适配动态JSON结构。
第一步:定义枚举类型
先把两类动态type定义为枚举,后续可以通过特性控制序列化输出为字符串而非数字:
// Source类型枚举 [JsonConverter(typeof(JsonStringEnumConverter))] public enum SourceType { Land, Water, Air } // 修改项类型枚举 [JsonConverter(typeof(JsonStringEnumConverter))] public enum ModifyType { Performance, Aesthetic, Functional }
第二步:定义多态基类和派生类
所有公共属性放在抽象基类,不同type独有的属性放在对应派生类,完全避免冗余字段:
Source相关类
// Source抽象基类,仅放公共字段 public abstract class SourceBase { public SourceType Type { get; set; } } // 陆地类型Source独有属性 public class LandSource : SourceBase { public string Speed { get; set; } } // 空中类型Source独有属性 public class AirSource : SourceBase { public string Height { get; set; } public string NumberLandings { get; set; } } // 水上类型Source独有属性 public class WaterSource : SourceBase { // 自行补充水上场景独有的属性 }
Modify修改项相关类
// 修改项抽象基类,仅放公共字段 public abstract class ModifyItemBase { public ModifyType Type { get; set; } } // 性能类修改项独有属性 public class PerformanceModify : ModifyItemBase { public string Brakes { get; set; } public string Turbo { get; set; } public string Suspension { get; set; } } // 功能类修改项独有属性 public class FunctionalModify : ModifyItemBase { public string Electric { get; set; } public string Applications { get; set; } } // 外观类修改项独有属性 public class AestheticModify : ModifyItemBase { // 自行补充外观场景独有的属性 }
第三步:实现自定义多态JSON转换器
以ASP.NET Core默认的System.Text.Json为例,写两个转换器自动根据type字段匹配对应派生类,完成序列化/反序列化:
Source类型转换器
public class SourceConverter : JsonConverter<SourceBase> { public override bool CanConvert(Type typeToConvert) => typeof(SourceBase).IsAssignableFrom(typeToConvert); public override SourceBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; var typeStr = root.GetProperty("type").GetString(); var sourceType = Enum.Parse<SourceType>(typeStr); return sourceType switch { SourceType.Land => JsonSerializer.Deserialize<LandSource>(root.GetRawText(), options), SourceType.Air => JsonSerializer.Deserialize<AirSource>(root.GetRawText(), options), SourceType.Water => JsonSerializer.Deserialize<WaterSource>(root.GetRawText(), options), _ => throw new NotSupportedException($"不支持的Source类型:{typeStr}") }; } public override void Write(Utf8JsonWriter writer, SourceBase value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, value.GetType(), options); } }
修改项类型转换器
public class ModifyItemConverter : JsonConverter<ModifyItemBase> { public override bool CanConvert(Type typeToConvert) => typeof(ModifyItemBase).IsAssignableFrom(typeToConvert); public override ModifyItemBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; var typeStr = root.GetProperty("type").GetString(); var modifyType = Enum.Parse<ModifyType>(typeStr); return modifyType switch { ModifyType.Performance => JsonSerializer.Deserialize<PerformanceModify>(root.GetRawText(), options), ModifyType.Functional => JsonSerializer.Deserialize<FunctionalModify>(root.GetRawText(), options), ModifyType.Aesthetic => JsonSerializer.Deserialize<AestheticModify>(root.GetRawText(), options), _ => throw new NotSupportedException($"不支持的修改项类型:{typeStr}") }; } public override void Write(Utf8JsonWriter writer, ModifyItemBase value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, value.GetType(), options); } }
第四步:注册转换器并定义顶层模型
注册转换器
在Program.cs中配置JSON选项,注册上述转换器适配系统自带的模型绑定:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new SourceConverter()); options.JsonSerializerOptions.Converters.Add(new ModifyItemConverter()); // 配置大小写不敏感,适配JSON驼峰命名 options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; });
顶层模型定义
public class RootModel { public PropertiesModel Properties { get; set; } } public class PropertiesModel { public SourceBase Source { get; set; } public List<ModifyItemBase> Modify { get; set; } }
方案优势
- 无冗余属性:每个派生类仅包含对应类型独有的字段,不会出现永远为null的无效属性
- 类型安全:通过枚举和派生类约束取值,编译期即可检查出类型错误
- 自动适配:转换器自动根据type字段匹配对应派生类,不需要手动处理转换逻辑
- 原生兼容:完全适配ASP.NET Core自带的模型绑定,接口入参直接用
RootModel接收即可
内容的提问来源于stack exchange,提问作者ach5
相关产品推荐
相关产品推荐

