C#中含接口类型属性的对象反序列化问题
问题场景
现有接口ISquare、ISide及其实现类Square、Side,其中Square类的属性Side为ISide类型,但构造函数接收的是具体Side类型参数。使用自定义ConcreteTypeConverter进行Json反序列化时,调用API传入指定JSON后触发错误:
System.InvalidOperationException: 'Each parameter in the deserialization constructor on type 'ProvaModel.Square' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. Fields are only considered when 'JsonSerializerOptions.IncludeFields' is enabled. The match can be case-insensitive.'
核心矛盾:需要保留属性的接口类型,但反序列化时无法自动将接口类型映射到具体实现类,导致构造函数参数绑定失败。
解决方案
方法1:修改构造函数参数类型为接口类型
直接将Square的构造函数参数类型从Side改为ISide,利用已标记在ISide上的JsonConverter特性自动完成具体类的反序列化:
internal class Square : ISquare { public ISide Side { get; } [JsonConstructor] internal Square(ISide side) // 将参数类型改为ISide { Side = side; } }
原理:ISide接口已标记[JsonConverter(typeof(ConcreteTypeConverter<Side>))],反序列化时JsonSerializer会自动将JSON中的side节点转换为Side实例,传给构造函数的ISide参数。此时参数名side与属性名Side(大小写不敏感)匹配,满足绑定要求,错误即可消除。
方法2:全局配置接口与具体类的映射
如果无法修改构造函数,可以在Json序列化配置中直接指定接口对应的具体实现类,无需依赖接口上的特性标记:
在Program.cs/Startup.cs的控制器配置中添加:
builder.Services.AddControllers() .AddJsonOptions(options => { // 为ISide指定具体实现类Side options.JsonSerializerOptions.Converters.Add(new JsonConverter<ISide>() { public override ISide? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return JsonSerializer.Deserialize<Side>(ref reader, options); } public override void Write(Utf8JsonWriter writer, ISide value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value as Side, options); } }); // 为ISquare指定具体实现类Square options.JsonSerializerOptions.Converters.Add(new JsonConverter<ISquare>() { public override ISquare? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return JsonSerializer.Deserialize<Square>(ref reader, options); } public override void Write(Utf8JsonWriter writer, ISquare value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value as Square, options); } }); });
这种方式更灵活,适合无法修改原有类结构的场景。
错误原因说明
原错误的本质是:Square构造函数要求传入Side类型参数,但反序列化时,根据ISquare的Side属性类型(ISide),JsonSerializer会尝试生成ISide实例,无法直接转换为构造函数需要的Side类型,同时参数与属性的类型不匹配导致绑定失败。通过让构造函数接收接口类型,或全局配置接口到具体类的映射,即可解决类型不兼容的问题。
内容的提问来源于stack exchange,提问作者Manuel Venè

