Json.Net反序列化时带参构造函数引发异常行为问题
问题原因与解决方案
原因分析
当Inner类使用带参的[JsonConstructor]时,Json.NET的反序列化逻辑会优先通过构造函数实例化对象:
- Json.NET先根据JSON字段匹配构造函数参数,创建
Inner实例,此时Inner的Value属性会通过类内初始值声明生成一个Value对象(对应输出里的第二个Value constructor)。 - 针对JSON中的
Value字段,Json.NET会尝试新建一个Value实例(对应输出里的第一个Value constructor),但由于Inner的Value属性是只读(仅含getter),Json.NET无法将新创建的Value实例赋值给该属性。 - 最终
outer.Inner.Value保留的是初始生成的Value对象,其num字段为默认值0,而非JSON中的1。
而当Inner使用无参构造函数时,Json.NET会采用「先实例化、再逐个填充属性」的逻辑:直接复用Inner实例中已初始化的Value对象,将JSON中的num值填充进去,结果符合预期。
解决方案
以下是几种可行的解决方式,可根据类设计需求选择:
方案1:给Value属性添加[JsonProperty]标记
即使Value是只读属性,添加[JsonProperty]后,Json.NET会识别到需要对已存在的实例进行填充,而非创建新实例:
public class Inner { [JsonConstructor] protected Inner(string random) { Console.WriteLine("Inner JSON constructor"); Random = random; } public string Random { get; } [JsonProperty] // 添加该标记 public Value Value { get; } = new(); }
修改后执行输出:
Value constructor Inner JSON constructor 1
方案2:在构造函数中添加Value参数
调整Inner的带参构造函数,加入Value类型的参数,让Json.NET在构造Inner时直接传入反序列化后的Value对象:
public class Inner { [JsonConstructor] protected Inner(string random, Value value) { Console.WriteLine("Inner JSON constructor"); Random = random; // 处理JSON未传入Value的情况,避免null Value = value ?? new Value(); } public string Random { get; } public Value Value { get; } }
这种方式下,Json.NET会直接将JSON中的Value数据反序列化为实例传入构造函数,最终num值会正确设置为1。
方案3:保留无参构造函数(若允许类设计变更)
如果业务允许Inner存在无参构造函数,Json.NET会自动采用属性填充逻辑,无需额外修改即可得到预期结果:
public class Inner { // 保留无参构造函数 public Inner() {} [JsonConstructor] // 可选,若需优先使用带参构造可保留,但需结合方案1或2 protected Inner(string random) { Console.WriteLine("Inner JSON constructor"); Random = random; } public string Random { get; } public Value Value { get; } = new(); }
内容的提问来源于stack exchange,提问作者stripies
相关产品推荐
相关产品推荐

