反序列化JSON至RequestWithDefault时虚拟集合字段元素重复问题
问题分析与解决方案
这个问题我之前也碰到过,核心要看你使用的序列化器类型,但本质都是反序列化过程中没有替换预先初始化的集合实例,而是把JSON中的元素追加到了已有的集合里,最终导致默认值"test"和JSON里的"f1"、"f2"共存,甚至触发重复元素的业务异常。
为什么会出现这个问题?
你的RequestWithDefault类里,Fields属性在声明时直接初始化了一个包含"test"的List<string>:
public override List<string> Fields {get; set; } = new List<string> {"test"}
- 如果你用的是Newtonsoft.Json(Json.NET):它的默认行为是,只要集合属性已经有实例(比如你这里预先初始化的List),反序列化时就会把JSON数组的元素逐个追加进去,而不是创建新集合替换掉原有实例。
- 如果你用的是DataContractSerializer:正常情况下,只要属性有public setter,它会创建新集合赋值给属性,但如果基类的virtual属性加上某些序列化配置,也可能出现集合被复用、元素追加的情况。
针对性解决方案
情况1:使用Newtonsoft.Json(Json.NET)
方案1:移除属性初始化器,让反序列化自动创建新集合
最简单的方式是去掉属性上的初始化代码,这样Json.NET会在反序列化时自动创建新的List<string>并填充JSON中的元素:
[DataContract] // 注意:这里应该是[DataContract]而非[DataContext],可能是笔误 public class RequestWithDefault : BaseRequest { [DataMember] public override List<string> Fields { get; set; } }
如果需要在JSON未传入fields字段时保留默认的["test"],可以把初始化逻辑放到无参构造函数里,同时配置JsonProperty确保只有当JSON中没有该字段时才使用默认值:
using Newtonsoft.Json; [DataContract] public class RequestWithDefault : BaseRequest { public RequestWithDefault() { Fields = new List<string> {"test"}; } [DataMember] [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public override List<string> Fields { get; set; } }
方案2:自定义转换器强制替换集合
如果必须保留属性初始化器,可以写一个自定义JsonConverter,强制在反序列化时创建新集合替换原有实例:
public class ReplaceCollectionConverter<T> : JsonConverter<List<T>> { public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer) { // 忽略已有集合,直接从JSON读取新集合 var newCollection = serializer.Deserialize<List<T>>(reader); return newCollection ?? new List<T>(); } public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
然后把转换器应用到Fields属性上:
[DataMember] [JsonConverter(typeof(ReplaceCollectionConverter<string>))] public override List<string> Fields { get; set; } = new List<string> {"test"};
情况2:使用DataContractSerializer
如果用的是.NET自带的DataContractSerializer,只需要调整初始化逻辑的位置,把属性初始化器移到构造函数里即可:
[DataContract] public class RequestWithDefault : BaseRequest { public RequestWithDefault() { Fields = new List<string> {"test"}; } [DataMember] public override List<string> Fields { get; set; } }
这样,当JSON中存在fields字段时,DataContractSerializer会创建新的集合实例赋值给Fields,覆盖构造函数的默认值;如果JSON中没有该字段,则保留默认的["test"]。
内容的提问来源于stack exchange,提问作者Emil
相关产品推荐
相关产品推荐

