ASP.NET Core如何用record构造函数解析ICollection实现类参数
[FromBody] 默认使用 System.Text.Json 执行请求体反序列化,其内置逻辑存在固定分支判断:当目标类型实现ICollection<T>接口时,会直接将其判定为集合类型,跳过普通CLR类型的构造函数匹配流程,尝试将传入的JSON数组直接映射为集合元素填充到实例中,完全不会识别record定义中构造函数的data参数。这就是移除ICollection<T>接口实现后,解析器回归普通record的构造函数绑定逻辑,参数就能正常解析的核心原因。
方案1:用特性显式指定反序列化使用的构造函数
这是改动最小的方案,直接在自定义集合类型上标记[JsonConstructor],强制解析器走指定构造函数完成绑定,绕过默认的集合解析逻辑:
using System.Text.Json.Serialization; // 注意原代码漏了泛型参数声明<T>,补上才能正常编译 public record collection<T>(ICollection<T> data) : ICollection<T> where T : Product { // 显式标记构造函数,告诉序列化器用这个入口实例化 [JsonConstructor] public collection(ICollection<T> data) : this(data) { } // 保留原有ICollection<T>接口的全部实现逻辑 // ...ICollection implementation methods here }
如果你的项目配置了Newtonsoft.Json作为默认JSON序列化器,将特性替换为[Newtonsoft.Json.JsonConstructor]即可,逻辑完全一致。
方案2:自定义JSON转换器接管该类型的解析流程
如果不希望在业务模型上引入序列化相关的特性,可以为collection<T>编写专用的JsonConverter,完全手动控制反序列化逻辑,强制读取JSON结构中的data字段传入构造函数:
using System.Text.Json; using System.Text.Json.Serialization; public class CollectionTypeConverter<T> : JsonConverter<collection<T>> where T : Product { public override collection<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 解析传入JSON节点,提取data字段的值反序列化为泛型集合 using var doc = JsonDocument.ParseValue(ref reader); var dataSet = doc.RootElement.GetProperty("data").Deserialize<ICollection<T>>(options); return new collection<T>(dataSet); } public override void Write(Utf8JsonWriter writer, collection<T> value, JsonSerializerOptions options) { // 序列化时保持和构造函数参数一致的结构输出 writer.WriteStartObject(); writer.WritePropertyName("data"); JsonSerializer.Serialize(writer, value.data, options); writer.WriteEndObject(); } }
转换器编写完成后,既可以直接通过特性标记到collection<T>类型上:
[JsonConverter(typeof(CollectionTypeConverter<>))] public record collection<T>(ICollection<T> data) : ICollection<T> where T : Product { // 接口实现逻辑 }
也可以全局注册到Program.cs的Mvc JsonOptions配置中,所有该类型的序列化/反序列化都会自动走自定义逻辑。
方案3:调整入参模型设计规避默认解析分支
如果不希望修改序列化配置,可以拆分入参模型和业务逻辑类型:作为接口入参的record类型不实现ICollection<T>接口,仅保留data属性做参数绑定,绑定完成后在业务代码中再转换为需要的集合实现类型,从根源上绕开序列化器的集合类型判定分支。
内容的提问来源于stack exchange,提问作者Ttech

