一:讲故事

上一篇介绍的 6 个特性从园子里的反馈来看效果不错,那这一篇就再带来 6 个特性同大家一起欣赏。

二:特性分析

1. 像弱类型语言一样解析 json

大家都知道弱类型的语言有很多,如: nodejs,python,php,它们有一个地方就是处理json,不需要像 强类型语言 那样还要给它配一个类,什么意思呢?就拿下面的 json 说事。


{
 "DisplayName": "新一代算法模型",
 "CustomerType": 1,
 "Report": {
   "TotalCustomerCount": 1000,
   "TotalTradeCount": 50
 },
 "CustomerIDHash": [1,2,3,4,5]
}

这个 json 如果想灌到 C# 中处理,你就得给它定义一个适配的类,就如 初篇 的客户算法模型类,所以这里就有了一个需求,能不能不定义类也可以自由解析上面这串 json 呢???哈哈,当然是可以的, 反序列化成 Dictionary 即可,就拿提取 Report.TotalCustomerCount 和 CustomerIDHash 这两个字段演示一下。


       static void Main(string[] args)
       
{
           var json = @"{
                          'DisplayName': '新一代算法模型',
                          'CustomerType': 1,
                          'Report': {
                            'TotalCustomerCount': 1000,
                            'TotalTradeCount': 50
                          },
                          'CustomerIDHash': [1,2,3,4,5]
                        }"
;

           var dict = JsonConvert.DeserializeObject<Dictionary<object, object>>(json);

           var report = dict["Report"] as JObject;
           var totalCustomerCount = report["TotalCustomerCount"];

           Console.WriteLine($"totalCustomerCount={totalCustomerCount}");

           var arr = dict["CustomerIDHash"] as JArray;
           var list = arr.Select(m => m.Value<int>()).ToList();

           Console.WriteLine($"list={string.Join(",", list)}");
       }

2. 如何让json中的枚举保持更易读的字符串型

这句话是什么意思呢?默认情况下, SerializeObject 会将 Model 中的 Enum 变成数值型,大家都知道数值型语义性是非常差的,如下代码所示:


   static void Main(string[] args)
   
{
       var model = new ThreadModel() { ThreadStateEnum = System.Threading.ThreadState.Running };

       var json = JsonConvert.SerializeObject(model);

       Console.WriteLine(json);
   }

   class ThreadModel
   {

       public System.Threading.ThreadState ThreadStateEnum { get; set; }
   }

对吧,确实语义特别差,那能不能直接生成 Running 这种字符串形式呢?当然可以了。。。改造如下:


 var json = JsonConvert.SerializeObject(model, new StringEnumConverter());

这里可能就有人钻牛角尖了,能不能部分指定让枚举生成 string,其他的生成 int ,没关系,这也难不倒我,哪里使用就用 JsonConverter 标记哪里。。。


       static void Main(string[] args)
       
{
           var model = new ThreadModel()
           {
               ThreadStateEnum = System.Threading.ThreadState.Running,
               TaskStatusEnum = TaskStatus.RanToCompletion
           };

           var json = JsonConvert.SerializeObject(model);

           Console.WriteLine(json);
       }

       class ThreadModel
       {

           public System.Threading.ThreadState ThreadStateEnum { get; set; }

           [JsonConverter(typeof(StringEnumConverter))]
           public TaskStatus TaskStatusEnum { get; set; }
       }

3. 格式化 json 中的时间类型

在 model 转化成 json 的过程中,总少不了 时间类型,为了让时间类型 可读性更高,通常会 格式化为 YYYY年/MM月/dd日 ,那如何实现呢?很简单撒,在 JsonConvert 中也是一个 枚举 帮你搞定。。。


       static void Main(string[] args)
       
{
           var json = JsonConvert.SerializeObject(new Order()
           {
               OrderTitle = "女装大佬",
               Created = DateTime.Now
           }, new JsonSerializerSettings
           {
               DateFormatString = "yyyy年/MM月/dd日",
           });

           Console.WriteLine(json);
       }
       public class Order
       {

           public string OrderTitle { get; set; }
           public DateTime Created { get; set; }
       }

对了,我记得很早的时候,C# 自带了一个 JavaScriptSerializer, 也是用来进行 model 转 json的,但是它会将 datetime 转成 时间戳,而不是时间字符串形式,如果你因为特殊原因想通过 JsonConvert 将时间生成时间戳的话,也是可以的, 用 DateFormatHandling.MicrosoftDateFormat 枚举指定一下即可,如下:

4. 对一些常用设置进行全局化

在之前所有演示的特性技巧中都是在 JsonConvert 上指定的,也就是说 100 个 JsonConvert 我就要指定 100 次,那有没有类似一次指定,整个进程通用呢?这么强大的 Newtonsoft 早就支持啦, 就拿上面的 Order 举例:


       JsonConvert.DefaultSettings = () =>
       {
           var settings = new JsonSerializerSettings
           {
               Formatting = Formatting.Indented
           };
           return settings;
       };

       var order = new Order() { OrderTitle = "女装大佬", Created = DateTime.Now };

       var json1 = JsonConvert.SerializeObject(order);
       var json2 = JsonConvert.SerializeObject(order);

       Console.WriteLine(json1);
       Console.WriteLine(json2);

可以看到,Formatting.Indented 对两串 json 都生效了。

5. 如何保证 json 到 model 的严谨性 及提取 json 未知字段

有时候我们有这样的需求,一旦 json 中出现 model 未知的字段,有两种选择:要么报错,要么提取出未知字段,在 Newtonsoft 中默认的情况是忽略,场景大家可以自己找哈。

  • 未知字段报错


       static void Main(string[] args)
       
{
           var json = "{'OrderTitle':'女装大佬', 'Created':'2020/6/23','Memo':'订单备注'}";

           var order = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
           {
               MissingMemberHandling = MissingMemberHandling.Error
           });

           Console.WriteLine(order);
       }

       public class Order
       {

           public string OrderTitle { get; set; }
           public DateTime Created { get; set; }
           public override string ToString()
           
{
               return $"OrderTitle={OrderTitle}, Created={Created}";
           }
       }

  • 提取未知字段

我依稀的记得 WCF 在这种场景下也是使用一个 ExtenstionDataObject 来存储客户端传过来的未知字段,有可能是客户端的 model 已更新,server端还是旧版本,通常在 json 序列化中也会遇到这种情况,在 JsonConvert 中使用 _additionalData 就可以帮你搞定,在 OnDeserialized 这种AOP方法中进行拦截,如下代码:


   static void Main(string[] args)
   
{
       var json = "{'OrderTitle':'女装大佬', 'Created':'2020/6/23','Memo':'订单备注'}";

       var order = JsonConvert.DeserializeObject<Order>(json);

       Console.WriteLine(order);
   }

   public class Order
   {

       public string OrderTitle { get; set; }

       public DateTime Created { get; set; }

       [JsonExtensionData]
       private IDictionary<string, JToken> _additionalData;

       public Order()
       
{
           _additionalData = new Dictionary<string, JToken>();
       }

       [OnDeserialized]
       private void OnDeserialized(StreamingContext context)
       
{
           var dict = _additionalData;
       }

       public override string ToString()
       
{
           return $"OrderTitle={OrderTitle}, Created={Created}";
       }
   }

6. 开启 JsonConvert 详细日志功能

有时候在查阅源码的时候开启日志功能更加有利于理解源码的内部运作,所以这也是一个非常实用的功能,看看如何配置吧。


       static void Main(string[] args)
       
{
           var json = "{'OrderTitle':'女装大佬', 'Created':'2020/6/23','Memo':'订单备注'}";

           MemoryTraceWriter traceWriter = new MemoryTraceWriter();

           var account = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
           {
               TraceWriter = traceWriter
           });

           Console.WriteLine(traceWriter.ToString());
       }

       public class Order
       {

           public string OrderTitle { get; set; }

           public DateTime Created { get; set; }

           public override string ToString()
           
{
               return $"OrderTitle={OrderTitle}, Created={Created}";
           }
       }

三:总结

嘿嘿,这篇 6 个特性就算说完了, 结合上一篇一共 12 个特性,是不是非常简单且实用,后面准备给大家带来一些源码解读吧! 希望本篇对您有帮助,谢谢!


©著作权归作者所有:来自51CTO博客作者mb5fd86a704dffe的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 配置文件中的数据库连接串加密了,你以为我就挖不出来吗?
  2. 快醒醒,C# 9 中又来了一堆关键词 init,record,with
  3. python入门教程13-04 (语法入门之记录相关操作)
  4. ALV显示红绿灯(FM ALV 和 OO ALV两种实现方式)
  5. FM实现F4帮助系列一:常规实现
  6. 小心BDC录屏带有行项目输入的坑
  7. FM实现F4帮助系列四:弹出框多筛选条件的搜索帮助(自定义筛选字段)
  8. OO ALV常用功能完整简例(热键单击,双击,帮助,编辑,自定义工具条等)
  9. FM实现F4帮助系列三:弹出框多筛选条件的搜索帮助(根据搜索帮助筛选

随机推荐

  1. Github标星19K+Star,10分钟自建对象存储服
  2. 前后端分离项目,如何优雅实现文件存储!
  3. 手把手教你搞定权限管理,结合Spring Secur
  4. 前后端分离项目,引入 Spring Cloud Gatewa
  5. 使用Seata彻底解决Spring Cloud中的分布
  6. 手把手教你搞定权限管理,结合Vue实现菜单
  7. mall在Linux环境下的自动化部署(基于Jenki
  8. 商品SKU功能设计与优化
  9. MySql主从复制,从原理到实践!
  10. 涵盖大部分核心组件使用的 Spring Cloud