最近在 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
進行比較來進行判斷是否存在集合中.
要寫客製化比較方式有兩種
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
預設值類型的比較是比較值.
預設參考類別比較的是地址.