首页 > 其他分享 >.net6&7中如何优雅且高性能的使用Json序列化

.net6&7中如何优雅且高性能的使用Json序列化

时间:2022-12-02 15:57:24浏览次数:65  
标签:生成 Json typeof Student JsonSerializable net6 序列化 public

.net中的SourceGenerator让开发者编可以写分析器,在项目代码编译时,分析器分析项目既有的静态代码,允许添加源代码到GeneratorExecutionContext中,一同与既有的代码参与编译。这种技术其实是把一些运行时才能去获取程序集相关资源的方式提前到编译前了。
.net6开始,微软为我们提供了System.Text.Json的SourceGenerator版本,接下来我们一起基于一个.net6的控制台项目学习了解System.Text.Json.SourceGenerator.
(SourceGenerator以下简称源生成)

目录

反射 vs 源生成

目前基本所有的序列化和反序列化都是基于反射,反射是运行时的一些操作,一直以来性能差而被诟病。System.Text.Json中的JsonSerializer对象中的序列化操作也是基于反射的,我们常用的方法如下:
序列化:

JsonSerializer.Serialize(student, new JsonSerializerOptions()
{
    WriteIndented = true, 
    PropertyNameCaseInsensitive = true //不敏感大小写
});

反序列化:

JsonSerializer.Deserialize<Student>("xxxx");

本身微软就宣称System.Text.Json.JsonSerializer性能是强于一个Newtonsoft,所以这两年一直使用微软自带的。
当然话题扯远了,只是带大家稍微了解回顾下。
我们来看看微软官网提供的反射和源生成两种方式在Json序列化中的优劣:

1.可以看到反射的易用性和开放程度是高于源生成的。
2.性能方面则是源生成完全碾压。

源生成注意点

1.源生成有两种模式:元数据收集和序列化优化,两者的区别会在下面的实践中给出自己的理解,官网并没有得到较为明确的两种的解释,两种生成模式可以同时存在。默认同时启用。
2.源生成不能够像反射一样可以使用JsonInclude标签将包含私有访问器的公共属性包含进来,会抛NotSupportedException异常

元数据收集&序列化优化

元数据收集

可以使用源生成将元数据收集进程从运行时移到编译时。 在编译期间,系统将收集元数据并生成源代码文件。 生成的源代码文件会自动编译为应用程序的一个整型部分。 使用此方法便无需进行运行时元数据集合,这可提高序列化和反序列化的性能.

序列化优化:

这个就比较好理解一点了,无非就是对于序列化的一些设置选项和特性做出一些优化,当然目前不是所有设置和特性都支持,官网也列出了受支持的设置和特性。

设置选项:

特性:

好了说了这么多,大家对一些概念都有了基本了解,我也很讨厌这么多文字的概念往上贴,那么现在就进入实战!

实战

创建项目

一个.net6的控制台项目,可以观察到它的分析器里有一个System.Text.Json.SourceGenerator这个解析器

创建一个序列化上下文

创建SourceGenerationContext派生自JsonSerializerContext

指定要序列化或反序列化的类型

通过向上下文类应用 JsonSerializableAttribute 来指定要序列化或反序列化的类型。
不需要为类型的字段类型做特殊处理,但是如果类型包含object类型的对象,并且你知道,在运行时,它可能有 boolean 和 int 对象
则需要添加

[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]

以增加对于这些类型的支持,便于源生成提前生成相关类型代码。

序列化配置

JsonSourceGenerationOptions可以添加一些序列化的配置设置。

序列化上下文最后代码:

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Student))] 
[JsonSerializable(typeof(Teacher))]
internal partial class SourceGenerationContext : JsonSerializerContext
{

}

分析器下会出现一些自动生成的代码:

序列化/反序列化

序列化:

JsonSerializer.Serialize(student, SourceGenerationContext.Default.Student);

反序列化:

var obj = JsonSerializer.Deserialize<Student>(
    jsonString, SourceGenerationContext.Default.Student);
指定源生成方式
元数据收集模式

全部类型设置元数据收集模式

[JsonSourceGenerationOptions(WriteIndented = true,GenerationMode =JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(Student))] 
[JsonSerializable(typeof(Teacher))]
internal partial class SourceGenerationContext : JsonSerializerContext
{

}

单个类型设置元数据收集模式,只设置学生类型使用特定的元数据收集模式

[JsonSourceGenerationOptions(WriteIndented = true,GenerationMode =JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(Student,GenerationMode =JsonSourceGenerationMode.Metadata))]
[JsonSerializable(typeof(Teacher))]
internal partial class SourceGenerationContext : JsonSerializerContext
{

}
序列化优化模式

全部类型设置序列化优化模式

[JsonSourceGenerationOptions(WriteIndented = true,GenerationMode =JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(Student))]
[JsonSerializable(typeof(Teacher))]
internal partial class SourceGenerationContext : JsonSerializerContext
{

}

单个类型设置序列化优化模式,只设置学生类型使用特定的序列化优化模式

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Student), GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(Teacher))]
internal partial class SourceGenerationContext : JsonSerializerContext
{

}

注意点:如果不显示设置源生成模式,那么会同时应用元数据收集和序列化优化两种方式。

效果对比

说了这么多,你凭啥说服我们使用这玩意儿??
我们试试使用JsonSerializer和源生成的方式来跑10000次序列化试试,说试就试,完整代码如下:

using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace DemoSourceGenerator
{
    public class Student
    {
        private int Id { get; set; }
        public string StuName { get; set; }
        public DateTime Birthday { get; set; }
        public string Address { get; set; }
    }

    public class Teacher 
    {
        public int Id { get; set; }
        public string TeacherName { get; set; }
        public DateTime Birthday { get; set; }
        public string Address { get; set; }
    }

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Student))]
    [JsonSerializable(typeof(Teacher))]
    internal partial class SourceGenerationContext : JsonSerializerContext
    {

    }

    public class Program 
    {
        public static void Main(string[] args) 
        {
            Student student = new Student()
            {
                StuName = "Bruce",
                Birthday = DateTime.Parse("1996-08-24"),
                Address = "上海市浦东新区"
            };

            Stopwatch stopwatch1 = new Stopwatch();
            stopwatch1.Start();
            foreach (var index in Enumerable.Range(0, 10000))
            {
                JsonSerializer.Serialize(student, new JsonSerializerOptions()
                {
                    WriteIndented = true,
                    PropertyNameCaseInsensitive = true
                });
            }
            stopwatch1.Stop();
            Console.WriteLine($"原始的序列化时间:{stopwatch1.ElapsedMilliseconds}");

            Stopwatch stopwatch2 = new Stopwatch();
            stopwatch2.Start();
            foreach (var index in Enumerable.Range(0, 10000))
            {
                JsonSerializer.Serialize(student, SourceGenerationContext.Default.Student);
            }
            stopwatch2.Stop();
            Console.WriteLine($"源码生成器的序列化时间:{stopwatch2.ElapsedMilliseconds}");
        }
    }
}

我们直接跑这个程序看看

跑了几次下来,时间上差距了接近300倍!!!

应用场景

1.首先肯定是.net 6及其之后的版本,因为我们公司在升级一些服务到.net6,所以可以使用微软提供的这个功能。
2.大量的使用到了序列化和反序列化,可以为建立一个上下文,将这这些类型通过JsonSerializable注册到上下文中,当然也可以根据领域划分多个上下文。

参考文档

https://learn.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json/source-generation-modes?pivots=dotnet-7-0

https://learn.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json/source-generation-modes?pivots=dotnet-7-0

本文是本人按照官方文档和自己的一些实际使用作出,如存在误区,希望不吝赐教。

标签:生成,Json,typeof,Student,JsonSerializable,net6,序列化,public
From: https://www.cnblogs.com/qwqwQAQ/p/16944672.html

相关文章

  • 645仪表以JSON格式上发方法
    1.概述之前我们已经介绍了ModbusRTU仪表实现JSON格式上发云服务器的方法,类似的现在也可以支持645协议的仪表通过JSON格式上发服务器。卓岚实现645仪表转JSON网关的特点......
  • jsoncpp serialize class in vector
    //Book.h#include<iostream>#include<jsoncpp/json/json.h>usingnamespacestd;classBook{public:intIdx;unsignedlonglongId;char*Name......
  • apache common包中的序列化工具
    什么是序列化我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术。Java序列化技术正是将对象转变成......
  • 使用.NET7和C#11打造最快的序列化程序-以MemoryPack为例
    译者注本文是一篇不可多得的好文,MemoryPack的作者neuecc大佬通过本文解释了他是如何将序列化程序性能提升到极致的;其中从很多方面(可变长度、字符串、集合等)解释了一......
  • 使用Fastjson作为http消息转换器
    主要是创建 FastJsonHttpMessageConverter的实例。@BeanpublicHttpMessageConvertersfastJsonHttpMessageConverters(){//1、定义一个convert转换消......
  • golang的jsonrpc客户端通用写法
    服务端packagemainimport( "errors" "fmt" "log" "net" "net/rpc" "net/rpc/jsonrpc" "os")//算数运算结构体typeArithstruct{}//算数运算请求结......
  • JSON端口操作实例
    JSON端口可直接实现在JSON和XML之间进行转换。端口会自动检测输入文件是JSON还是XML,然后将文件在两种格式间相互转换。该端口较多的是运用在API接口调用集成方案......
  • xml 转 json
    https://www.cnblogs.com/ZXdeveloper/p/16500959.htmlhttp://t.zoukankan.com/javalinux-p-14021005.html......
  • [Json] GSON 数据容错
    GSONGSON是Googel公司开发的用于解析json的类库。可以很轻松地让程序员将java对象转换成JSON格式,或者将JSON格式的对象转换成Java对象。GSON的github地址:https://github.......
  • Go实现整合 Logrus 输出json格式日志
    学习与交流:Go语言技术微信群商务合作加微信:LetsFenggoland全家桶激活码,支持所有版本,支持所有系统链接:http://web.52shizhan.cn/activity/s2abxc提取码:GJF9B1DK 现......