JsonConvert.SerializeObject 呼叫 object.Equals 問題探討

最近在 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 進行比較來進行判斷是否存在集合中.

要寫客製化比較方式有兩種

  1. 在.net中每個類別都繼承於ObjectObject 中有object.Equals 所以可以重寫object.Equals方法.
  2. 將此類別實現 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

預設值類型的比較是比較值.
預設參考類別比較的是地址.


如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^