在开始本文以前,真的好想作个问卷调查,到底有多少人和我同样,对 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
类,目前来看功能还不算太丰富,简单用用仍是能够的,本篇就说到这里,但愿对您有帮助。