栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

对 JsonConvert 的认识太肤浅了,终于还是遇到了问题

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

对 JsonConvert 的认识太肤浅了,终于还是遇到了问题

一:背景 1. 讲故事

在开始本文之前,真的好想做个问卷调查,到底有多少人和我一样,对 JsonConvert 的认识只局限在 SerializeObject 和 DeserializeObject 这两个方法上(┬_┬), 这样我也好结伴同行,不再孤单落魄,或许是这两个方法基本上能够解决工作中 80% 的场景,对于我来说确实是这样,但随着编码的延续,终究还是会遇到那剩下的 20% ,所以呀。。。

我的场景是这样的:前段时间写业务代码的时候,我有一个自定义的客户算法类型的Model,这个Model中有这种算法类型下的客户群以及Report统计信息,还用了 HashSet 记录了该类型下的 CustomerID集合,为了方便讲述,我把Model简化如下:


    class CustomerAlgorithmModel
    {
 public string DisplayName { get; set; }

 public int CustomerType { get; set; }

 public ReprotModel Report { get; set; }

 public HashSet 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(Enumerable.Range(1, 500000))
     };

     var json = JsonConvert.SerializeObject(algorithModel);

     File.WriteAllText("1.txt", json, Encoding.UTF8);

     Console.WriteLine("写入完成!");
 }

可以看到,仅一个json就 3.3M,这样的记录多来几打后,在 kibana 上一检索,浏览器就卡的要死,其实 CustomerIDHash 这个字段对我来说是可有可无的,就算存下来了也没啥大用,所以需求就来了,如何屏蔽掉 CustomerIDHash。

二:寻求解决方案 1. 使用 JsonIgnore

有问题就网上搜啊,这一搜马上就有人告诉你可以使用 JsonIgnoreAttribute 忽略特性,加好这个特性后继续跑一下程序。


    [Newtonsoft.Json.JsonIgnore]
    public HashSet CustomerIDHash { get; set; }

太好了,终于搞定了,但是静下心来想一想,总感觉心里有那么一点不舒服,为什么这么说,一旦你给这个 CustomerIDHash 套上了 JsonIgnore ,这就意味着它在 JsonConvet 的世界中从此消失,也不管是谁在使用这个Model, 但这并不是我的初衷,我的初衷仅仅是为了在记录日志的时候踢掉 CustomerIDHash,可千万不要影响在其他场景下的使用哈,现在这种做法就会给自己,给别人挖坑,埋下了不可预知的bug,我想你应该明白我的意思,还得继续寻找下一个方案。

2. 使用自定义的 JsonConverter

真的,Newtonsoft 太强大了,我都想写一个专题好好弥补弥补我的知识盲区,其实在这个场景中不就是想把 HashSet 给屏蔽掉嘛,Newtonsoft 中专门提供了一个针对特定类型的自定义处理类,接下来我就写一段:


   /// 
   /// 自定义一个 针对 HashSet 的转换类
   /// 
   public class HashSetConverter : Newtonsoft.Json.JsonConverter>
   {
public override HashSet ReadJson(JsonReader reader, Type objectType, HashSet existingValue, bool hasExistingValue, JsonSerializer serializer)
{
    return existingValue;
}

public override void WriteJson(JsonWriter writer, HashSet value, JsonSerializer serializer)
{
    writer.WriteNull();
}
   } 

就是这么简单,然后就可以在 SerializeObject 的时候指定下自定义的 HashSetConverter 即可,然后再将程序跑起来看一下。


 var json = JsonConvert.SerializeObject(algorithModel, Formatting.Indented, new HashSetConverter());

从图中看,貌似也是解决了,但我突然发现自己要钻牛角尖了,如果我的实体中又来了一个顶级优质客户群的 TopNCustomerIDHash,但因为这个CustomerID 比较少,我希望在 Json 中能保留下来,然后就是踢掉的那个 CustomerIDHash 我要保留 CustomerIDHash.Length,哈哈,搞事情哈,那接下来怎么解决呢?

  • 修改 Model 实体

    class CustomerAlgorithmModel
    {
 public HashSet CustomerIDHash { get; set; }

 // topN 优质客户群
 public HashSet TopNCustomerIDHash { get; set; }
    }

  • HashSetConverter 增加逻辑鉴别是否为保留字段

 public override void WriteJson(JsonWriter writer, HashSet value, JsonSerializer serializer)
 {
     if (writer.Path == "TopNCustomerIDHash")
     {
  writer.WriteStartArray();

  foreach (var item in value)
  {
      writer.Writevalue(item);
  }

  writer.WriteEndArray();
     }
     else
     {
  writer.Writevalue(value.Count);
     }
 }

  • 最后给 TopNCustomerIDHash 赋值

     var algorithModel = new CustomerAlgorithmModel()
     {
  CustomerType = 1,
  DisplayName = "",
  Report = new ReprotModel()
  {
      TotalCustomerCount = 1000,
      TotalTradeCount = 50
  },
  CustomerIDHash = new HashSet(Enumerable.Range(1, 500000)),
  TopNCustomerIDHash = new HashSet(Enumerable.Range(1, 10)),
     };

三块都搞定后就可以把程序跑起来了,如下图:

貌似钻牛角尖的问题是解决了,既然钻牛角尖肯定要各种鄙视,比如这里的 ReportModel 我是不需要的,CustomerType 我也是不需要的,我仅仅需要看一下 DisplayName 和 TotalCustomerCount 这两个字段就可以了, 那这个要怎么解决呢?

3. 使用 匿名类型

确实很多时候记日志,就是为了跟踪 Model 中你特别关心的那几个字段,所以掺杂了多余的字段确实也是没必要的,这里可以用匿名来解决,我就来写一段代码:


    var json = JsonConvert.SerializeObject(new
    {
 algorithModel.DisplayName,
 algorithModel.Report.TotalCustomerCount
    }, Formatting.Indented);

三: 总结

虽然阻击了几个回合,但同时也发现了 Newtonsoft 中还有特别多的未挖掘功能,真的需要好好研究研究,源码已下好,接下来准备做个系列来解剖一下,值得一提的是 .Net中已自带了 System.Text.Json.JsonSerializer 类,目前来看功能还不算太丰富,简单用用还是可以的,本篇就说到这里,希望对您有帮助。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号