Newtonsoft JSON序列化器无法反序列化含C#属性初始化器的集合
我之前在使用Json.NET处理带属性初始化器的对象集合时,也碰到过类似的坑——尤其是当属性初始化器和反序列化的赋值逻辑冲突的时候。结合你提供的PaymentLineItem代码片段,我来梳理下问题原因和解决办法:
问题分析
从你注释掉的PayCode = PayCodeDto.Invalid;来看,大概率是当这个属性初始化器存在时,反序列化集合时要么PayCode始终保留Invalid值(没被JSON中的数据覆盖),要么直接抛出反序列化异常。这通常是因为C#属性初始化器的执行时机和Json.NET的反序列化流程之间的冲突:C#的属性初始化器会在对象构造函数执行完毕后立即运行,而Json.NET是先创建对象(触发初始化器),再逐个赋值JSON中的属性。如果PayCodeDto本身的序列化配置有问题(比如没有无参构造函数),就会导致赋值失败。
解决方案
1. 确保PayCodeDto有正确的序列化配置
Json.NET反序列化自定义对象时,要求目标类必须有公共无参构造函数,否则无法实例化对象。先检查PayCodeDto的定义:
public class PayCodeDto { // 必须添加公共无参构造函数 public PayCodeDto() { } public static PayCodeDto Invalid { get; } = new PayCodeDto { // 这里写Invalid实例的初始化逻辑,比如Code = "INVALID" }; // 确保需要序列化的属性是可读写的 public string Code { get; set; } public string Name { get; set; } }
2. 显式标记属性的序列化行为
如果想保留属性初始化器(比如当JSON中没有PayCode字段时,默认用Invalid),可以通过JsonProperty特性明确告诉Json.NET如何处理这个属性:
public class PaymentLineItem { public DateTime TimeStamp { get; set; } public decimal Amount { get; set; } public string Description { get; set; } // 标记该属性可被序列化/反序列化,非必填 [JsonProperty(Required = Required.Default)] public PayCodeDto PayCode { get; set; } = PayCodeDto.Invalid; // 你的ToString、Equals、GetHashCode方法... }
这样配置后,如果JSON中存在PayCode字段,就会覆盖初始化器的值;如果没有,就保留Invalid。
3. 用专用构造函数控制反序列化逻辑
如果初始化逻辑比较复杂,或者你想完全掌控反序列化时的对象创建过程,可以使用JsonConstructor特性指定反序列化专用的构造函数:
public class PaymentLineItem { public DateTime TimeStamp { get; set; } public decimal Amount { get; set; } public string Description { get; set; } public PayCodeDto PayCode { get; set; } // 正常创建对象时用的构造函数,保留默认初始化逻辑 public PaymentLineItem() { PayCode = PayCodeDto.Invalid; } // 反序列化时专用的构造函数,Json.NET会优先调用这个 [JsonConstructor] public PaymentLineItem(DateTime timeStamp, decimal amount, string description, PayCodeDto payCode) { TimeStamp = timeStamp; Amount = amount; Description = description; // 只有当JSON中的payCode为null时,才用Invalid值 PayCode = payCode ?? PayCodeDto.Invalid; } // 你的ToString、Equals、GetHashCode方法... }
这种方式能完全避免初始化器和反序列化赋值的冲突,逻辑更清晰。
4. 检查集合反序列化的代码
最后确认你的反序列化代码是否正确,比如:
// 示例:反序列化List<PaymentLineItem> string jsonString = "你的JSON字符串"; var paymentLines = JsonConvert.DeserializeObject<List<PaymentLineItem>>(jsonString);
如果JSON格式是单个对象而非数组,或者字段名大小写不匹配(Json.NET默认是驼峰匹配,但可以配置),也会导致反序列化失败,这时候需要检查JSON结构是否和集合类型匹配。
内容的提问来源于stack exchange,提问作者Klaus Nji

