本文内容来自我写的开源电子书《WoW C#》,现在正在编写中,可以去WOW-Csharp/学习路径总结.md at master · sogeisetsu/WOW-Csharp (github.com)来查看编写进度。预计2021年年底会完成编写,2022年2月之前会完成所有的校对和转制电子书工作,争取能够在2022年将此书上架亚马逊。编写此书的目的是因为目前.NET市场相对低迷,很多优秀的书都是基于.NET framework框架编写的,与现在的.NET 6相差太大,正规的.NET 5学习教程现在几乎只有MSDN,可是MSDN虽然准确优美但是太过琐碎,没有过阅读开发文档的同学容易一头雾水,于是,我就编写了基于.NET 5的《WoW C#》。本人水平有限,欢迎大家去本书的开源仓库sogeisetsu/WOW-Csharp关注、批评、建议和指导。

Json解析

json是一种类似于通过键值对来储存数据的格式,在对数据库进行操作的时候,通常会把类数据转为json格式,然后储存在数据库里面,使用的时候再将json转为类的实例化对象。java的springboot框架的一整套解决方案里面可以通过mybatis和fastjson完成这个操作。在web的前后端数据传输中,一般也是用json作为数据的载体,JavaScript有着对json比较完备的支持。

Json格式概述

  • 基础

    1. 概念: JavaScript Object Notation JavaScript对象表示法
    • json现在多用于存储和交换文本信息的语法

    • 进行数据的传输

    • JSON 比 XML 更小、更快,更易解析。

    1. 语法:

    2. 基本规则

    -  数据在名称/值对中:json数据是由键值对构成的
    
    -  键用引号(单双都行)引起来,也可以不使用引号
    
    -  值得取值类型:
    
      1. 数字(整数或浮点数)
    
      2. 字符串(在双引号中)
    
      3. 逻辑值(true 或 false)
    
      4. 数组(在方括号中)	{"persons":[{},{}]}
    
      5. 对象(在花括号中) {"address":{"province":"陕西"....}}
    
      6. null
    
    -  数据由逗号分隔:多个键值对由逗号分隔
    
    -  花括号保存对象:使用{}定义json 格式
    
    -  方括号保存数组:[]
    1. JavaScript获取数据:
  1. json对象.键名

  2. json对象["键名"]

  3. 数组对象[索引]

  4. 遍历

解析

使用 C# 对 JSON 进行序列化和反序列化 - .NET | Microsoft Docs

会用到两个名词,序列化和反序列化,其中序列化是指将实例对象转换成json格式的字符串,反序列化则是逆向前面序列化的过程。

在序列化的过程中,默认情况下会只序列化公共读写的属性,可以通过System.Text.Json.SerializationJsonInclude特性或者JsonSerializerOptionsIncludeFields属性来包含公有字段。通过System.Text.Json.SerializationJsonInclude特性可以来自定义可以序列化的非公共属性访问器(即属性的访问修饰符为public,但是set访问器和get访问器的任意一方为非public)。这可能对使用惯了java的人来说不适应,事实上这是一种很合理的序列化要求,默认状况下,序列化器会序列化对象中的所有可读属性,反序列化所有可写属性,这种方式尊重了访问修饰符的作用。也可用开源的Newtonsoft.Json来序列化非公有属性。现在很多编程语言(包括.NET)能通过反射来获取私有属性本身就是不合理的,从.NET core能明显的感觉到.NET团队出于安全的考虑在限制反射的使用。

需要用到的namespace

using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;
  • System.Text.JsonJsonSerializer.Serialize方法可以进行序列化
  • System.Text.JsonJsonSerializer.Deserialize方法可以进行反序列化
  • System.Text.Json.Serialization可以要序列化的类添加必要的特性,比如JsonPropertyName为属性序列化时重命名,再比如JsonInclude来定义序列化时要包含的字段。
  • System.Text.Encodings.WebSystem.Text.Unicode来让特定的字符集在序列化的时候能够正常序列化而不是被转义成为 \uxxxx,其中 xxxx 为字符的 Unicode 代码。事实上,默认情况下,序列化程序会转义所有非 ASCII 字符。

序列化

只将实例化对象转变成json字符串,假设有一个实例化对象weatherForecast,序列化方式如下:

string jsonString = JsonSerializer.Serialize(weatherForecast);

反序列化

指将json字符串序列化成实例化对象,书接前文,方式如下:

weatherForecast = JsonSerializer.Deserialize<WeatherForecastWithPOCOs>(jsonString);

JsonSerializerOptions

可以通过JsonSerializerOptions来指定诸如是否整齐打印和忽略Null值属性等信息。使用方式为将JsonSerializerOptions实例化之后再当作JsonSerializer.SerializeJsonSerializer.Deserialize的参数。

关于JsonSerializerOptions的属性可以查看如何使用 System.Text.Json 实例化 JsonSerializerOptions | Microsoft Docs

先实例化一个JsonSerializerOptions对象,在初始化器里面定义各种属性

JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions()
{
// 整齐打印
WriteIndented = true,
// 忽略值为Null的属性
IgnoreNullValues = true,
// 设置Json字符串支持的编码,默认情况下,序列化程序会转义所有非 ASCII 字符。 即,会将它们替换为 \uxxxx,其中 xxxx 为字符的 Unicode
// 代码。 可以通过设置Encoder来让生成的josn字符串不转义指定的字符集而进行序列化 下面指定了基础拉丁字母和中日韩统一表意文字的基础Unicode 块
// (U+4E00-U+9FCC)。 基本涵盖了除使用西里尔字母以外所有西方国家的文字和亚洲中日韩越的文字
Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs),
// 反序列化不区分大小写
PropertyNameCaseInsensitive = true,
// 驼峰命名
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 对字典的键进行驼峰命名
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
// 序列化的时候忽略null值属性
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
// 忽略只读属性,因为只读属性只能序列化而不能反序列化,所以在以json为储存数据的介质的时候,序列化只读属性意义不大
IgnoreReadOnlyFields = true,
// 不允许结尾有逗号的不标准json
AllowTrailingCommas = false,
// 不允许有注释的不标准json
ReadCommentHandling = JsonCommentHandling.Disallow,
// 允许在反序列化的时候原本应为数字的字符串(带引号的数字)转为数字
NumberHandling = JsonNumberHandling.AllowReadingFromString,
// 处理循环引用类型,比如Book类里面有一个属性也是Book类
ReferenceHandler = ReferenceHandler.Preserve
};

然后在序列化和反序列化的时候jsonSerializerOptions对象当作参数传给JsonSerializer.SerializeJsonSerializer.Deserialize

string jsonBookA = JsonSerializer.Serialize(bookA, jsonSerializerOptions);
// 反序列化
BookA bookA1 = JsonSerializer.Deserialize<BookA>(jsonBookA, jsonSerializerOptions);

JsonSerializerOptions 常用属性概述

作用 值类型
WriteIndented 整齐打印,将此值设置为true后序列化的json字符串在打印的时候会进行自动缩进和换行。默认为false。 bool
IgnoreNullValues 忽略值为Null的属性。默认为false。 bool
Encoder 设置Json字符串支持的编码,默认情况下,序列化程序会转义所有非 ASCII 字符。 即,会将它们替换为 \uxxxx,其中 xxxx 为字符的 Unicode代码。 可以通过设置Encoder来让生成的josn字符串不转义指定的字符集而进行序列化。可设置为Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs)来包含除使用西里尔字母以外所有西方国家的文字和亚洲中日韩越的文字 JavaScriptEncoder
PropertyNameCaseInsensitive 反序列化不区分键的大小写。默认为false。 bool
PropertyNamingPolicy 序列化时属性的命名方式,常用的为JsonNamingPolicy.CamelCase设置成小写字母开头的驼峰命名。 JsonNamingPolicy
DictionaryKeyPolicy 序列化时对字典的string键进行小写字母开头的驼峰驼峰命名。 JsonNamingPolicy
DefaultIgnoreCondition 指定一个条件,用于确定何时在序列化或反序列化过程中忽略具有默认值的属性。 默认值为 Never。常用值为JsonIgnoreCondition.WhenWritingDefault来忽略默认值属性。 JsonIgnoreCondition
IgnoreReadOnlyFields 序列化时忽略只读属性,因为只读属性只能序列化而不能反序列化,所以在以json为储存数据的介质的时候,序列化只读属性意义不大。默认为false。 bool
AllowTrailingCommas 反序列化时,允许结尾有逗号的不标准json,默认为false。 bool
ReadCommentHandling 反序列化时,允许有注释的不标准json,默认为false。 bool
NumberHandling 使用NumberHandling = JsonNumberHandling.AllowReadingFromString可允许在反序列化的时候原本应为数字的字符串(带引号的数字)转为数字 JsonNumberHandling
ReferenceHandler 配置在读取和写入 JSON 时如何处理对象引用。使用ReferenceHandler = ReferenceHandler.Preserve仍然会在序列化和反序列化的时候保留引用并处理循环引用。 ReferenceHandler
IncludeFields 确定是否在序列化和反序列化期间处理字段。 默认值为 false bool

System.Text.Json.Serialization 特性

可以为将要序列化和被反序列化而生成的类的属性和字段添加特性。

JsonInclude 包含特定public字段和非公共属性访问器

在序列化或反序列化时,使用 JsonSerializerOptions.IncludeFields 全局设置或 [JsonInclude] 特性来包含字段(必须是public),当应用于某个属性时,指示非公共的 getter 和 setter 可用于序列化和反序列化。 不支持非公共属性。

demo:

/// <summary>
/// 时间戳
/// </summary>
[JsonInclude]
public long timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); /// <summary>
/// 书的名称
/// </summary>
[JsonInclude]
public string Name { private get; set; } = "《书名》";

JsonPropertyName 自定义属性名称

若要设置单个属性的名称,请使用 [JsonPropertyName] 特性。

此特性设置的属性名称:

  • 同时适用于两个方向(序列化和反序列化)。
  • 优先于属性命名策略。

demo:

/// <summary>
/// 作者
/// </summary>
[JsonPropertyName("作者")]
public string Author
{
get { return _author; }
set { _author = value; }
}

JsonIgnore 忽略单个属性

阻止对属性进行序列化或反序列化。

demo:

/// <summary>
/// 书的出版商
/// </summary>
[JsonIgnore]
public string OutCompany { get => _outCompany; set => _outCompany = value; }

JsonExtensionData 处理溢出 JSON

反序列化时,可能会在 JSON 中收到不是由目标类型的属性表示的数据。可以将这些无法由目标类型的属性表示的数据储存在一个Dictionary<string, JsonElement>字典里面,方式如下:

/// <summary>
/// 储存反序列化时候的溢出数据
/// </summary>
[JsonExtensionData]
public Dictionary<string, JsonElement> ExtensionData { get; set; }

笔者的选择

在笔者的开发经验当中,json用的最多的就是前后端数据传输和数据库储存数据。对jsonSerializerOptions往往会选择这几个选项:

JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions()
{
// 整齐打印
WriteIndented = true,
// 忽略值为Null的属性
IgnoreNullValues = true,
// 设置Json字符串支持的编码,默认情况下,序列化程序会转义所有非 ASCII 字符。 即,会将它们替换为 \uxxxx,其中 xxxx 为字符的 Unicode
// 代码。 可以通过设置Encoder来让生成的josn字符串不转义指定的字符集而进行序列化 下面指定了基础拉丁字母和中日韩统一表意文字的基础Unicode 块
// (U+4E00-U+9FCC)。 基本涵盖了除使用西里尔字母以外所有西方国家的文字和亚洲中日韩越的文字
Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs, UnicodeRanges.CjkSymbolsandPunctuation),
// 反序列化不区分大小写
PropertyNameCaseInsensitive = true,
// 驼峰命名
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
// 对字典的键进行驼峰命名
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
// 忽略只读属性,因为只读属性只能序列化而不能反序列化,所以在以json为储存数据的介质的时候,序列化只读属性意义不大
IgnoreReadOnlyFields = true,
// 允许在反序列化的时候原本应为数字的字符串(带引号的数字)转为数字
NumberHandling = JsonNumberHandling.AllowReadingFromString
};

尽量不使用JsonPropertyName特性,对有可能会用到json反序列化的类一定会用到JsonExtensionData特性来储存可能存在的溢出数据。JsonIgnoreJsonInclude会广泛的使用而不用JsonSerializerOptionsIncludeFields来序列化所有字段。

LICENSE

已将所有引用其他文章之内容清楚明白地标注,其他部分皆为作者劳动成果。对作者劳动成果做以下声明:

copyright 2021 苏月晟,版权所有。


本作品由苏月晟采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

c# System.Text.Json 精讲的更多相关文章

  1. 【译】System.Text.Json 的下一步是什么

    .NET 5.0 最近发布了,并带来了许多新特性和性能改进.System.Text.Json 也不例外.我们改进了性能和可靠性,并使熟悉 Newtonsoft.Json 的人更容易采用它.在这篇文章中 ...

  2. [译]试用新的System.Text.Json API

    译注 可能有的小伙伴已经知道了,在.NET Core 3.0中微软加入了对JSON的内置支持. 一直以来.NET开发者们已经习惯使用Json.NET这个强大的库来处理JSON. 那么.NET为什么要增 ...

  3. 使用System.Text.Json处理Json文档以及部分坑

    System.Text.Json处理Json文档需要用到JsonDocument,JsonElement,JsonProperty. JsonDocument就是一个表示Json文档的东西,JsonE ...

  4. 在.Net Core 3.0中尝试新的System.Text.Json API

    .NET Core 3.0提供了一个名为System.Text.Json的全新命名空间,它支持reader/writer,文档对象模型(DOM)和序列化程序.在此博客文章中,我将介绍它如何工作以及如何 ...

  5. .netcore3.0 System.Text.Json 日期格式化

    .netcore3.0 的json格式化不再默认使用Newtonsoft.Json,而是使用自带的System.Text.Json来处理. 理由是System.Text.Json 依赖更少,效率更高. ...

  6. In .net 4.8,calculate the time cost of serialization in BinaryFormatter,NewtonSoft.json,and System.Text.Json.JsonSerializer.Serialize

    using ConsoleApp390.Model; using Newtonsoft.Json; using System; using System.Collections.Generic; us ...

  7. C# Serialization performance in System.Runtime.Serialization.Formatters.Binary.BinaryFormatter,Newtonsoft.Json.JsonConvert and System.Text.Json.JsonSerializer.Serialize

    In .net core 3.0 using System;using System.Collections.Generic;using System.Collections;using System ...

  8. .NET Core 内置的 System.Text.Json 使用注意

    System.Text.Json 是 .NET Core 3.0 新引入的高性能 json 解析.序列化.反序列化类库,武功高强,但毕竟初入江湖,炉火还没纯青,使用时需要注意,以下是我们在实现使用中遇 ...

  9. Net core 2.x 升级 3.0 使用自带 System.Text.Json 时区 踩坑经历

    .Net Core 3.0 更新的东西很多,这里就不多做解释了,官方和博园大佬写得很详细 关于 Net Core 时区问题,在 2.1 版本的时候,因为用的是 Newtonsoft.Json,配置比较 ...

随机推荐

  1. 宙斯盾 DDoS 防护系统“降本增效”的云原生实践

    作者 tomdu,腾讯云高级工程师,主要负责宙斯盾安全防护系统管控中心架构设计和后台开发工作. 导语 宙斯盾 DDoS 防护系统作为公司级网络安全产品,为各类业务提供专业可靠的 DDoS/CC 攻击防 ...

  2. git 修改最后一次提交

    git 修改最后一次提交 有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了. 此时,可以运行带有 --amend 选项的提交命令来重新提交:git commit --amend -m ...

  3. 【Spring】IoC容器 - Spring Bean作用域Scope(含SpringCloud中的RefreshScope )

    前言 上一章学习了[依赖来源],本章主要讨论SpringBean的作用域,我们这里讨论的Bean的作用域,很大程度都是默认只讨论依赖来源为[Spring BeanDefinition]的作用域,因为在 ...

  4. 这12种场景Spring事务会失效!

    前言 对于从事java开发工作的同学来说,spring的事务肯定再熟悉不过了.在某些业务场景下,如果一个请求中,需要同时写入多张表的数据.为了保证操作的原子性 (要么同时成功,要么同时失败),避免数据 ...

  5. Java多线程中的死锁

    Java多线程中的死锁 死锁产生的原因 线程死锁是指由两个以上的线程互相持有对方所需要的资源,导致线程处于等待状态,无法往前执行. 当线程进入对象的synchronized代码块时,便占有了资源,直到 ...

  6. SpringBoot小知识点

    记录SpringBoot的小知识点 一.在 Spring 上下文刷新之前设置一些自己的环境变量 1.实现 EnvironmentPostProcessor 接口 2.spring.factories ...

  7. Java 将Word保存为WPS和WPT格式

    本文通过Java示例展示将Word文档(如.doc/.docx)保存为WPS和WPT格式的方法. 程序环境配置 IntelliJ IDEA 2018(jdk 1.8.0) Word Jar包:Spir ...

  8. HTML+CSS基础(HTML篇)

    引言 在日常开发Android中,很多时候会遇到和WebView打交道,对CSS HTML JS不是很清楚的话是完不成一些功能的,本篇开始学习HTML,文章的主要内容是总结了慕课网中,HTML+CSS ...

  9. 第01课 OpenGL窗口(1)

    教程的这一节在2000年一月彻底重写了一遍.将会教您如何设置一个 OpenGL窗口.它可以只是一个窗口或是全屏幕的.可以任意 大小.任意色彩深度.此处的代码很稳定且很强大,您可以在您所有的OpenGL ...

  10. Windows内核基础知识-5-调用门(32-Bit Call Gate)

    Windows内核基础知识-5-调用门(32-Bit Call Gate) 调用门有一个关键的作用,就是用来提权.调用门其实就是一个段. 调用门: 这是段描述符的结构体,里面的s字段用来标记是代码段还 ...