最近在 StackOverFlow 解答一个很有趣的问题Json.Net / Newtonsoft: Using JsonConvert.SerializeObject results in weird .Equals calls - why?
问题简述是:
使用Newtonsoft.Json.JsonConvert.SerializeObject
方法 来把物件转成JSON
资料时,为什么会呼叫物件的Equals
方法 且传入的object obj
类型不是此类别类型,而是属性的类型
以下是发问者提供的程式码:
public class JsonTestClass{ public string Name { get; set; } public List<int> MyIntList { get; set; } public override bool Equals(object obj) { if (obj == null) return false; JsonTestClass jtc = (JsonTestClass)obj; return true; }}JsonTestClass c = new JsonTestClass();c.Name = "test";c.MyIntList = new List<int>();c.MyIntList.Add(1);string json = JsonConvert.SerializeObject(c, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
看到问题后我就直接去看Json.net原始码 一探到底原因出在哪边.
后面发现当我们在呼叫JsonConvert.SerializeObject
方法,会执行一个 private bool CheckForCircularReference私有方法.
bool exists = (Serializer._equalityComparer != null) ? _serializeStack.Contains(value, Serializer._equalityComparer) : _serializeStack.Contains(value);
这个方法主要用意是判断目前序列化JSON物件是否有重複引用本身,方法中有段程式码使用到 List<T>.Contains
.
当我们在呼叫List<T>.Contains
时 预设EqualityComparer<T>.Default
进行比较来进行判断是否存在集合中.
要写客製化比较方式有两种
在.net中每个类别都继承于Object
, Object
中有object.Equals
所以可以重写object.Equals
方法.将此类别实现 IEquatable<T>
并重写你要的比较方式.所以会呼叫object.Equals
是因为上段程式码
什么是判断目前序列化JSON物件是否有重複引用本身?
以下的範例是private bool CheckForCircularReference想要防止的问题
public class JsonTestClass{ public string Name { get; set; } public List<int> MyIntList { get; set; } public JsonTestClass Test{get;set;}}JsonTestClass c = new JsonTestClass();c.Name = "test";c.Test = c;string json = JsonConvert.SerializeObject (c, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
我们可以看到c.Test = c;
将自己本身付值给 public JsonTestClass Test{get;set;}
这个属性.
我们执行上面程式码会得到此错误
Self referencing loop detected for property 'Test' with type 'Program+JsonTestClass'. Path ''.
是因为他要防止重複引用本身导致无限迴圈解析JSON
.
Note
预设值类型的比较是比较值.
预设参考类别比较的是地址.