.NET6中序列化含大小写同名属性的匿名类型为JSON时为何抛出异常?
从.NET Core 3.1迁移到.NET 6后,序列化包含大小写同名属性的匿名类型时抛出异常,测试代码如下:
var obj = new { input = new { foo = "foo" }, INPUT = new { foo = "foo" } }; var json = JsonSerializer.Serialize(obj); // .NET 6中抛出异常
.NET Core 3.1可正常输出:
{"input":{"foo":"foo"},"INPUT":{"foo":"bar"}}
.NET 6抛出异常:
Unhandled exception. System.InvalidOperationException:
Members 'input' and 'INPUT' on type
'<>f__AnonymousType02[<>f__AnonymousType11[System.String],<>f__AnonymousType1`1[System.String]]'
cannot both bind with parameter 'input' in the deserialization constructor.
将匿名类型改为普通类后可正常序列化:
public class Obj { public Input input { get; set; } public Input INPUT { get; set; } } public class Input { public string foo { get; set; } }
var obj = new Obj(){ input = new Input() { foo = "bar" }, INPUT = new Input() { foo = "bar" }}; var json = JsonSerializer.Serialize(obj);
输出结果:
{"input":{"foo":"bar"},"INPUT":{"foo":"bar"}}
1. .NET 6中System.Text.Json的匿名类型处理逻辑变更
.NET 6对System.Text.Json进行了升级,其中一个关键变化是:序列化匿名类型时,会预先检查反序列化的兼容性——系统会自动为匿名类型生成用于反序列化的构造函数绑定规则,而这个规则对参数名的匹配是大小写不敏感的。
2. 匿名类型的编译器特性触发冲突
C#匿名类型由编译器自动生成,每个属性对应构造函数的参数。当匿名类型中存在input和INPUT两个仅大小写不同的属性时,System.Text.Json在生成反序列化绑定规则时,会认为这两个属性都应该绑定到同一个构造函数参数(匹配时忽略大小写),从而触发“无法同时绑定到同一参数”的冲突,抛出异常。
3. .NET Core 3.1无异常的原因
在.NET Core 3.1中,System.Text.Json对匿名类型的序列化仅完成JSON输出,未加入反序列化兼容性的预检查逻辑,因此不会检测到这种大小写属性的绑定冲突,直接输出JSON结果。
4. 普通类无异常的原因
普通类不存在编译器自动生成的反序列化构造函数(除非手动定义并标记),System.Text.Json序列化普通类时,直接遍历并输出属性字段,不需要进行构造函数参数绑定的检查,因此大小写不同的属性可以正常序列化。
5. 大小写敏感设置无效的原因
JsonSerializerOptions.PropertyNameCaseInsensitive是用于反序列化时匹配JSON属性到类成员的配置,而当前异常发生在序列化阶段的反序列化兼容性预检查中,因此调整该配置无法解决此冲突。
内容的提问来源于stack exchange,提问作者Dave Baghdanov

