本文内容来自我写的开源电子书《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.Serialization的JsonInclude特性或者JsonSerializerOptions的IncludeFields属性来包含公有字段。通过System.Text.Json.Serialization的JsonInclude特性可以来自定义可以序列化的非公共属性访问器(即属性的访问修饰符为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.Json的JsonSerializer.Serialize方法可以进行序列化。
  • 用System.Text.Json的JsonSerializer.Deserialize方法可以进行反序列化。
  • 用System.Text.Json.Serialization可以要序列化的类添加必要的特性,比如JsonPropertyName为属性序列化时重命名,再比如JsonInclude来定义序列化时要包含的字段。
  • 用System.Text.Encodings.Web和System.Text.Unicode来让特定的字符集在序列化的时候能够正常序列化而不是被转义成为 \uxxxx,其中 xxxx 为字符的 Unicode 代码。事实上,默认情况下,序列化程序会转义所有非 ASCII 字符。

序列化

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

string jsonString = JsonSerializer.Serialize(weatherForecast);

反序列化

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

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

JsonSerializerOptions

可以通过JsonSerializerOptions来指定诸如是否整齐打印和忽略Null值属性等信息。使用方式为将JsonSerializerOptions实例化之后再当作JsonSerializer.Serialize和JsonSerializer.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.Serialize和JsonSerializer.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特性来储存可能存在的溢出数据。JsonIgnore 和JsonInclude会广泛的使用而不用JsonSerializerOptions的IncludeFields来序列化所有字段。

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. python socket zmq

    本篇博客将介绍zmq应答模式,所谓应答模式,就是一问一答,规则有这么几条 1. 必须先提问,后回答 2. 对于一个提问,只能回答一次 3. 在没有收到回答前不能再次提问 上代码,服务端: #codin ...

  2. 【数据结构与算法Python版学习笔记】树——相关术语、定义、实现方法

    概念 一种基本的"非线性"数据结构--树 根 枝 叶 广泛应用于计算机科学的多个领域 操作系统 图形学 数据库 计算机网络 特征 第一个属性是层次性,即树是按层级构建的,越笼统就越 ...

  3. 力扣 - 剑指 Offer 53 - I. 在排序数组中查找数字 I

    题目 剑指 Offer 53 - I. 在排序数组中查找数字 I 思路1 一般来说,首先想到的是使用一个变量,从头开始遍历整个数组,记录target数组出现的次数,但是这样的时间复杂度是O(n),还是 ...

  4. Seata分布式事务失败通知

    一.背景 在我们使用Seata作为分布式事务时,有些时候我们的分布式时候并不是每次都可以成功的,而对于这些失败的分布式事务就需要进行通知.这篇文章简单记录一下如何实现通知. 二.功能实现 此处模拟邮件 ...

  5. 算法:数字推盘游戏--重排九宫(8-puzzle)

    一.数字推盘游戏 数字推盘游戏(n-puzzle)是一种最早的滑块类游戏,常见的类型有十五数字推盘游戏和八数字推盘游戏等.也有以图画代替数字的推盘游戏.可能Noyes Palmer Chapman在1 ...

  6. hdu 1754 I Hate It(单点更新,区段查最值)

    题意: N个成绩.M个操作. Q a b:查询第a个到第b个成绩中最高成绩 U a b:将第a个成绩改成b 思路: 看代码,, 代码: const int maxn = 200010; int max ...

  7. oracle 修改表空间名

    1.登录使用sys用户登录 sqlplus sys/ as sysdba 2.修改表空间名字 SQL> alter tablespace 旧表空间名 rename to 新表空间名; 表空间已更 ...

  8. echarts 让轴自适应数据为小数整数

    echarts 让轴自适应数据为小数整数,以解决y轴数值重复的问题 工作中突然遇到这个问题 试了一下用formatter自适应  ok  在yAxis中提阿尼按键属性 axisLabel 1 axis ...

  9. 基于 Istio 的全链路灰度方案探索和实践

    作者|曾宇星(宇曾) 审核&校对:曾宇星(宇曾) 编辑&排版:雯燕 背景 微服务软件架构下,业务新功能上线前搭建完整的一套测试系统进行验证是相当费人费时的事,随着所拆分出微服务数量的不 ...

  10. Redis6.2发布 地理位置功能增强了什么?

    原文地址:https://developer.aliyun.com/article/780257 Redis社区最近刚刚发布Redis6.2 RC1版本,在本次发布中,阿里云Tair团队(阿里云云内存 ...