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類型不是此類別類型,而是屬性的類型

以下是發問者提供的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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私有方法.

1
2
3
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中每個類別都繼承於Object, Object 中有object.Equals 所以可以重寫object.Equals方法.
  2. 將此類別實現 IEquatable<T> 並重寫你要的比較方式.

所以會呼叫object.Equals是因為上段程式碼


補充說明:

什麼是判斷目前序列化JSON物件是否有重複引用本身?

以下的範例是private bool CheckForCircularReference想要防止的問題

1
2
3
4
5
6
7
8
9
10
11
12
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

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

此文作者:Daniel Shih(石頭)
此文地址https://isdaniel.github.io/jsonconvert-serializeobject/
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!


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