🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

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 許可協議。轉載請註明出處!

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