.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

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

.net6&7中如何优雅且高性能的使用Json序列化的更多相关文章

  1. .net core3.1 web api中使用newtonsoft替换掉默认的json序列化组件

    在微软的文档中,有着较为详细的替换教程 https://docs.microsoft.com/zh-cn/aspnet/core/web-api/advanced/formatting?view=as ...

  2. [译]Golang中的优雅重启

    原文 Graceful Restart in Golang 作者 grisha 声明:本文目的仅仅作为个人mark,所以在翻译的过程中参杂了自己的思想甚至改变了部分内容,其中有下划线的文字为译者添加. ...

  3. React中如何优雅的捕捉事件错误

    React中如何优雅的捕捉事件错误 前话 人无完人,所以代码总会出错,出错并不可怕,关键是怎么处理. 我就想问问大家react的错误怎么捕捉呢? 这个时候: 小白:怎么处理? 小白+: ErrorBo ...

  4. Python项目中如何优雅的import

    Python项目中如何优雅的import 前言 之前有一篇关于Python编码规范的随笔, 但是写的比较杂乱, 因为提到了import语句, 在篇文章中, 我专门来讲Python项目中如何更好的imp ...

  5. 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转

    线程安全使用(四)   这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...

  6. Java 8 中如何优雅的处理集合

    Java 8 中如何优雅的处理集合(Stream API) 在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增.删.改.查.聚合.统计.过滤等操作.相比之下,关系型数据库中也同样有 ...

  7. 在Asp.NET Core中如何优雅的管理用户机密数据

    在Asp.NET Core中如何优雅的管理用户机密数据 背景 回顾 在软件开发过程中,使用配置文件来管理某些对应用程序运行中需要使用的参数是常见的作法.在早期VB/VB.NET时代,经常使用.ini文 ...

  8. SpringBoot中如何优雅的使用多线程

    SpringBoot中如何优雅的使用多线程 当异步方法有返回值时,如何获取异步方法执行的返回结果呢?这时需要异步调用的方法带有返回值CompletableFuture

  9. C#中JSON序列化和反序列化

    有一段时间没有到博客园写技术博客了,不过每天逛逛博客园中大牛的博客还是有的,学无止境…… 最近在写些调用他人接口的程序,用到了大量的JSON.XML序列化和反序列化,今天就来总结下json的序列化和反 ...

  10. 超高性能的json序列化之MVC中使用Json.Net

    先不废话,直接上代码 Asp.net MVC自带Json序列化 /// <summary> /// 加载组件列表 /// </summary> /// <param na ...

随机推荐

  1. Django 测试脚本

    一.测试脚本 Django 在创建项目时自动在应用下创建了tests.py,这个py文件可以作为测试文件:也可以在应用下手动创建一个py测试文件. 无论哪种方式,都需要提前书写以下代码. from d ...

  2. Django 之必知必会三板斧

    一.HttpResponse 在django.http 模块中定义了HttpResponse 对象的API,HttpRequest 对象由Django 自动创建,不调用模板,直接返回数据. 1 在 a ...

  3. MinIO Python Client SDK 快速入门指南

    官方文档地址:http://docs.minio.org.cn/docs/master/python-client-quickstart-guide MinIO Python Client SDK提供 ...

  4. 记录Gerrit2.8.4环境迁移、安装、配置以及问题解决

    转载自:https://cloud.tencent.com/developer/article/1010629 说到gerrit,没听说的同学可能会感到比较陌生,那么先来copy一段关于gerrit的 ...

  5. C++ 自学笔记 对象的初始化

    数组的初始化: 在 C++中  struct ≈ Class:struct里面可以有函数. 默认构造函数: 没有参数的构造函数就是默认构造函数

  6. ETL工具Datax、sqoop、kettle 的区别

    一.Sqoop主要特点: 1.可以将关系型数据库中的数据导入到hdfs,hive,hbase等hadoop组件中,也可以将hadoop组件中的数据导入到关系型数据库中: 2.sqoop在导入导出数据时 ...

  7. C#中Math.Round()实现中国式四舍五入问题

    C#中的Math.Round()并不是使用的"四舍五入"法.实际上是四舍六入五取偶. 对于这个问题我们常见的解决方式是使用MidpointRounding.AwayFromZero ...

  8. Linux-->vi和vim编辑器的基本操作

    vim编辑器介绍 vi或者vim就是对linux下的文本进行编辑的一种编辑器比如说a.cpp文件这种 Linux会内置vi文本编辑器 Vim可以简单的认为vi的增强版 Linux是区分大小写的! 用法 ...

  9. Vue学习之--------Vue中收集表单数据(使用v-model 实现双向数据绑定、代码实现)(2022/7/18)

    文章目录 1.Vue中实现表单数据的收集 1.1 基础知识 1.2 代码实例 1.3 测试效果 1.4 额外插一嘴 1.Vue中实现表单数据的收集 1.1 基础知识 表单中常用的标签:input(输入 ...

  10. 在vue中的form表单中下拉框中的数据来自数据库查询到的数据

    文章目录 1.实现的效果: 2.前端html代码 3.js中的代码 4.后端的方法 1.实现的效果: 增加一个新的类型到数据库 2.前端html代码 需要注意的部分:prop后边是表单中的字段 v-m ...