在開始本文以前,真的好想作個問卷調查,到底有多少人和我同樣,對 JsonConvert 的認識只侷限在 SerializeObject
和 DeserializeObject
這兩個方法上(┬_┬), 這樣我也好結伴同行,再也不孤單落魄😁😁😁,或許是這兩個方法基本上可以解決工做中 80% 的場景,對於我來講確實是這樣,但隨着編碼的延續,終究仍是會遇到那剩下的 20% ,因此呀。。。算法
個人場景是這樣的:前段時間寫業務代碼的時候,我有一個自定義的客戶算法類型的Model,這個Model中有這種算法類型下的客戶羣以及Report統計信息,還用了 HashSet 記錄了該類型下的 CustomerID集合,爲了方便講述,我把Model簡化以下:json
class CustomerAlgorithmModel { public string DisplayName { get; set; } public int CustomerType { get; set; } public ReprotModel Report { get; set; } public HashSet<int> CustomerIDHash { get; set; } } class ReprotModel { public int TotalCustomerCount { get; set; } public int TotalTradeCount { get; set; } }
那有意思的就來了,我我的是有記日誌的癖好,就想着之後不會出現死無對證的狀況,而後就理所固然的使用 JsonConvert.SerializeObject
, 這一下就出問題了,日誌送入到了 ElasticSearch
,而後經過 Kibana
查不出來,爲啥呢? 看完上面的 Model 我想你也猜到了緣由,json體太大了哈,好歹 CustomerIDHash
中也有幾十萬個撒,這一下全導出成json了,這 size 還能小嗎? 要不我寫段代碼看一看。瀏覽器
static void Main(string[] args) { var algorithModel = new CustomerAlgorithmModel() { CustomerType = 1, DisplayName = "🐮👃", Report = new ReprotModel() { TotalCustomerCount = 1000, TotalTradeCount = 50 }, CustomerIDHash = new HashSet<int>(Enumerable.Range(1, 500000)) }; var json = JsonConvert.SerializeObject(algorithModel); File.WriteAllText("1.txt", json, Encoding.UTF8); Console.WriteLine("寫入完成!"); }
能夠看到,僅一個json就 3.3M
,這樣的記錄多來幾打後,在 kibana
上一檢索,瀏覽器就卡的要死,其實 CustomerIDHash
這個字段對我來講是無關緊要的,就算存下來了也沒啥大用,因此需求就來了,如何屏蔽掉 CustomerIDHash
。ide
有問題就網上搜啊,這一搜立刻就有人告訴你可使用 JsonIgnoreAttribute
忽略特性,加好這個特性後繼續跑一下程序。編碼
[Newtonsoft.Json.JsonIgnore] public HashSet<int> CustomerIDHash { get; set; }
太好了,終於搞定了,可是靜下心來想想,總感受內心有那麼一點不舒服,爲何這麼說,一旦你給這個 CustomerIDHash
套上了 JsonIgnore
,這就意味着它在 JsonConvet 的世界中今後消失,也不論是誰在使用這個Model, 但這並非個人初衷,個人初衷僅僅是爲了在記錄日誌的時候踢掉 CustomerIDHash
,可千萬不要影響在其餘場景下的使用哈,如今這種作法就會給本身,給別人挖坑,埋下了不可預知的bug,我想你應該明白個人意思,還得繼續尋找下一個方案。日誌
真的,Newtonsoft
太強大了,我都想寫一個專題好好彌補彌補個人知識盲區,其實在這個場景中不就是想把 HashSet<int>
給屏蔽掉嘛,Newtonsoft
中專門提供了一個針對特定類型的自定義處理類,接下來我就寫一段:code
/// <summary> /// 自定義一個 針對 HashSet<int> 的轉換類 /// </summary> public class HashSetConverter : Newtonsoft.Json.JsonConverter<HashSet<int>> { public override HashSet<int> ReadJson(JsonReader reader, Type objectType, HashSet<int> existingValue, bool hasExistingValue, JsonSerializer serializer) { return existingValue; } public override void WriteJson(JsonWriter writer, HashSet<int> value, JsonSerializer serializer) { writer.WriteNull(); } }
就是這麼簡單,而後就能夠在 SerializeObject
的時候指定下自定義的 HashSetConverter
便可,而後再將程序跑起來看一下。orm
var json = JsonConvert.SerializeObject(algorithModel, Formatting.Indented, new HashSetConverter());
從圖中看,貌似也是解決了,但我忽然發現本身要鑽牛角尖了,若是個人實體中又來了一個頂級優質客戶羣的 TopNCustomerIDHash
,但由於這個CustomerID 比較少,我但願在 Json 中能保留下來,而後就是踢掉的那個 CustomerIDHash 我要保留 CustomerIDHash.Length
,哈哈,搞事情哈,那接下來怎麼解決呢?blog
class CustomerAlgorithmModel { public HashSet<int> CustomerIDHash { get; set; } // topN 優質客戶羣 public HashSet<int> TopNCustomerIDHash { get; set; } }
public override void WriteJson(JsonWriter writer, HashSet<int> value, JsonSerializer serializer) { if (writer.Path == "TopNCustomerIDHash") { writer.WriteStartArray(); foreach (var item in value) { writer.WriteValue(item); } writer.WriteEndArray(); } else { writer.WriteValue(value.Count); } }
var algorithModel = new CustomerAlgorithmModel() { CustomerType = 1, DisplayName = "🐮👃", Report = new ReprotModel() { TotalCustomerCount = 1000, TotalTradeCount = 50 }, CustomerIDHash = new HashSet<int>(Enumerable.Range(1, 500000)), TopNCustomerIDHash = new HashSet<int>(Enumerable.Range(1, 10)), };
三塊都搞定後就能夠把程序跑起來了,以下圖:圖片
貌似鑽牛角尖的問題是解決了,既然鑽牛角尖確定要各類鄙視,好比這裏的 ReportModel 我是不須要的,CustomerType 我也是不須要的,我僅僅須要看一下 DisplayName
和 TotalCustomerCount
這兩個字段就能夠了, 那這個要怎麼解決呢?
確實不少時候記日誌,就是爲了跟蹤 Model 中你特別關心的那幾個字段,因此摻雜了多餘的字段確實也是不必的,這裏能夠用匿名來解決,我就來寫一段代碼:
var json = JsonConvert.SerializeObject(new { algorithModel.DisplayName, algorithModel.Report.TotalCustomerCount }, Formatting.Indented);
雖然阻擊了幾個回合,但同時也發現了 Newtonsoft
中還有特別多的未挖掘功能,真的須要好好研究研究,源碼已下好,接下來準備作個系列來解剖一下,值得一提的是 .Net
中已自帶了 System.Text.Json.JsonSerializer
類,目前來看功能還不算太豐富,簡單用用仍是能夠的,本篇就說到這裏,但願對您有幫助。