使用Newtonsoft.Json序列化反序列化后出现数据丢失问题求助
Newtonsoft.Json序列化反序列化后数据丢失原因分析
我在使用Newtonsoft.Json时遇到序列化反序列化后数据丢失的问题,复现代码如下:
internal class Program { private static JsonSerializerSettings Setting = new Newtonsoft.Json.JsonSerializerSettings() { PreserveReferencesHandling = PreserveReferencesHandling.Objects, }; static void Main(string[] args) { var script = new SubClass(); script.Param.Add(new Parameter() { msg = "I will die" }); var obj = new Class(); obj.subObj = script; string json; string json2; json = JsonConvert.SerializeObject(obj, Setting); Console.WriteLine("before:"); Console.WriteLine(json); obj = Newtonsoft.Json.JsonConvert.DeserializeObject<Class>(json, Setting); json2 = JsonConvert.SerializeObject(obj, Setting); Console.WriteLine("after:"); Console.WriteLine(json2); } public class Class { public IEnumerable<Parameter> parameters => subObj?.parameters(); public SubClass subObj { get; set; } } public class Parameter { public string msg { get; set; } } public class SubClass { public List<Parameter> Param { get; set; } = new List<Parameter>(); public IEnumerable<Parameter> parameters() { return Param; } } }
运行后两次序列化结果差异明显:
第一次序列化(反序列化前):
{"$id":"1","parameters":[{"$id":"2","msg":"I will die"}],"subObj":{"$id":"3","Param":[{"$ref":"2"}]}}
第二次序列化(反序列化后):
{"$id":"1","parameters":[null],"subObj":{"$id":"2","Param":[null]}}
删除Class类的parameters属性后,问题消失,运行结果恢复正常。
问题根源
这个问题的核心是**PreserveReferencesHandling.Objects设置与只读计算属性的序列化顺序冲突**:
序列化时的引用跟踪:
启用PreserveReferencesHandling.Objects后,Json.NET会为每个唯一对象分配$id,重复引用的对象会用$ref指向对应$id。第一次序列化时,Class.parameters返回的是subObj.Param中的同一个Parameter实例,所以Json.NET给这个实例分配$id=2,subObj.Param里的元素用$ref=2引用,序列化结果正常。反序列化时的顺序问题:
Json.NET反序列化是按照JSON属性的顺序处理的:- 先处理
Class的parameters属性,遇到{"$ref":"2"}时,$id=2对应的Parameter实例还未被创建(因为它在subObj.Param里,而subObj属性在JSON中排在parameters之后,还未处理)。 - Json.NET无法解析未创建对象的引用,只能用
null填充。 - 后续处理
subObj的Param属性时,再次遇到{"$ref":"2"},此时对象缓存中仍然没有$id=2的实例,只能再次填充null。
- 先处理
只读计算属性的影响:
Class.parameters是一个只读的计算属性(只有get逻辑,没有setter),Json.NET反序列化时无法将JSON值赋值给它,但仍会尝试解析对应的JSON数据。这个无效的解析过程破坏了引用跟踪的逻辑,最终导致两次序列化后数据丢失。
解决思路
- 如果你需要保留
parameters属性,可以给它添加[JsonIgnore]特性,让Json.NET序列化和反序列化时忽略这个属性,避免干扰引用跟踪。 - 调整JSON属性的序列化顺序,让
subObj属性先于parameters被处理(可以用[JsonProperty(Order = -1)]给subObj设置更高的优先级)。
内容的提问来源于stack exchange,提问作者dsjzazs
相关产品推荐
相关产品推荐

