【C# 序列化】Json序列化器
从 Newtonsoft.Json 迁移到 System.Text.Json
一.写在前面
System.Text.Json 是 .NET Core 3 及以上版本内置的 Json 序列化组件,刚推出的时候经常看到踩各种坑的吐槽,现在经过几个版本的迭代优化,提升了易用性,修复了各种问题,是时候考虑使用 System.Text.Json 了。本文将从使用层面来进行对比。
System.Text.Json 在默认情况下十分严格,避免进行任何猜测或解释,强调确定性行为。比如:字符串默认转义,默认不允许尾随逗号,默认不允许带引号的数字等,不允许单引号或者不带引号的属性名称和字符串值。 该库是为了实现性能和安全性而特意这样设计的。Newtonsoft.Json
默认情况下十分灵活。
关于性能,参考 Incerry 的性能测试:.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json ,如果打算使用 .NET 7 不妨考虑一下 System.Text.Json。
Newtonsoft.Json 使用 13.0.2 版本,基于 .NET 7。
两者对比着使用,请看 https://www.cnblogs.com/stulzq/p/17118904.html
命名空间
System.Text.Json:默认情况下,System.Text.Json使用运行时反射来收集它需要访问序列化和反序列化对象的属性的元数据。作为一种替代方法,System.Text.Json 6.0可以使用c#源生成器特性来提高性能,减少私有内存的使用,并简化程序组装,从而减少应用程序大小。例如在asp.net中可以读取 XML、JSON 直接生成 C# 代码参与编译,DTO 编写全自动化都是没问题的。编译时反射 - 0 运行时开销。
.NET 6 中的 System.Text.Json 已做了许多改进,因此它现在是一个“工业强度”的序列化解决方案。
源生成器
.NET 6 为 添加了新的源生成器。 源生成可与 JsonSerializer 配合使用,并且可以通过多种方式进行配置。 它可以提高性能、减少内存使用量并便于程序集剪裁。 有关详细信息,请参阅如何在 System.Text.Json 中选择反射或源生成以及如何在 System.Text.Json 中使用源生成。
System.Text.Json.Nodes
System.Text.Json.Serialization
JSON简介
JSON 是一种数据格式,已成为 XML 的流行替代品。它简单而整洁,语法类似于 JavaScript 对象。实际上,术语 JSON 代表 JavaScript Object Notation。.NET 的最新版本为处理 JSON 数据提供了内置支持。
System.Text.Json 命名空间提供高性能、低分配的功能来处理 JSON 数据。这些功能包括将对象序列化为 JSON 并将 JSON 反序列化为对象。它还提供用于创建内存中文档对象模型 (DOM) 的类型,用于访问 JSON 文档中的任何元素,提供数据的结构化视图。
支持 System.Runtime.Serialization
特性。System.Text.Json
中不支持 System.Runtime.Serialization 命名空间中的特性。
详细请查看 【C# 序列化】 JSON-在.net croe中的应用
Text.Json支持的格式
JSON 数据的书写格式是:名称/值对,数据由逗号分隔。.net core 对json 的处理默认使用驼峰式,
JSON 值可以是:
数字(整数或浮点数)
字符串(在双引号中)
逻辑值(true 或 false)
数组(在方括号中)[,]
对象(在花括号中){}
null
public class Serializeables { public DateTimeOffset Date { get; set; } public int TemperatureCelsius { get; set; } public string Ser = "first ser"; public bool Sers = true; public bool[] Serbool = { true ,true,true}; public List<SerrializePeople> SerList = new(); } //输出 /*{ "Date": "0001-01-01T00:00:00+00:00", "TemperatureCelsius": 0, "Ser": "first ser", "Sers": true, "Serbool": [true, true, true], "SerList": [{ "Person": null }, { "Person": null }] } */
Json序列化器 简介
功能
了具有以下功能的JsonSerializer
:
- 支持对简单传统CLR对象(POCO)、基元类型、集合进行序列化和反序列化;
- 内置的异步序列化和反序列化;
- 原生处理UTF-8数据;
- 反序列化可选是否区分大小写;
- 使用
JsonNamingPolicy.CamelCase
的驼峰命名策略; - 使用
JsonNamingPolicy
指定自定义命名策略; - 转义的JSON数据反序列化;
- 序列化时可选的最小字符转义;
- 序列化时忽略空值;
- 反序列化时忽略注释;
- 允许(JSON)尾随逗号;
- 可定制转换器;
- 使用
[JsonExtensionData]
特性指示在反序列化时没有匹配成员的属性存放处(a property overflow bag);(注:当属性的类型为IDictionary<TKey,TValue>
时,没有匹配成员的任何属性都会在反序列化期间添加到该字典中,并在序列化期间中写入) - 使用
[JsonIgnore]
特性忽略特定的属性; - 使用
[JsonProperty]
特性来指定自定义属性名。(注:未使用[JsonProperty]
特性时Json对应的实体类属性名需要与Json对应属性完全一致,使用[JsonProperty]
特性标识PropertyName
后,实体类属性名可自定义)
序列化选项设置
- 忽略属性
- 忽略单个属性,使用[JsonIgnore] 特性和JsonIgnoreCondition枚举类忽略当个属性
- 忽略所有只读属性
- 忽略所有 null 值属性
- 忽略所有默认值属性
- 允许无效的 JSON
- 处理溢出 JSON,使用 JsonElement 或 JsonNode
- 保留引用,处理循环引用
- 保留引用并处理循环引用 将元数据属性写入 (
$id
、$values
和$ref
)ReferenceHandler = ReferenceHandler.Preserve,
- 跨多个序列化和反序列化调用保留引用元数据(不理解,赶紧用不到)
- 忽略循环引用
ReferenceHandler = ReferenceHandler.IgnoreCycles,
- 保留引用并处理循环引用 将元数据属性写入 (
- 反序列化为不可变类型,非公共访问器
- 多态序列化 本文介绍的是序列化,而不是反序列化。 不支持多态反序列化,解决方法是,你编写一个自定义转换器,例如支持多态反序列化中的示例。不过已经不需要写反序列化类型,.net6.0可以直接使用Writable Dom。
- 反序列化 字符字符编码问题
序列化控制
Text.Json新增了4个接口
IJsonOnDeserialized代替[OnSerialized]
IJsonOnDeserializing代替[OnSerializing]
IJsonOnSerialized代替[OnDeserialized]
IJsonOnSerializing代替[OnDeserializing]
IJsonOnDeserializing, IJsonOnDeserialized, IJsonOnSerializing, IJsonOnSerialized public class Serializeables : IJsonOnDeserializing, IJsonOnDeserialized,IJsonOnSerializing, IJsonOnSerialized { public bool[] Serbool = { true, true, true }; public void OnSerializing() => Console.WriteLine("序列化中"); public void OnDeserializing() => Console.WriteLine("反序列化中"); public void OnDeserialized() => Console.WriteLine("反序列化后"); public void OnSerialized() => Console.WriteLine("序列化后"); }
[JsonIgnore] 特性 忽略属性
“序列化通知”功能对于设置默认值和验证属性值合法性非常有用。
JsonIgnoreCondition Enum
Always 1 属性将始终被忽略。
Never 0 属性将始终被序列化和反序列化,无论IgnoreNullValues配置如何。无论 DefaultIgnoreCondition
、IgnoreReadOnlyProperties
和 IgnoreReadOnlyFields
全局设置如何,始终序列化和反序列化属性。
WhenWritingDefault 2 属性只有在为空时才会被忽略。如果属性是引用类型 null
可为 null 的值类型 null
或值类型 default
,则在序列化中忽略属性。
WhenWritingNull 3 如果该值为空,则在序列化期间忽略该属性。这只适用于引用类型的属性和字段。WhenWritingNull
- 如果属性是引用类型 null
或可为 null 的值类型 null
,则在序列化中忽略属性。
public class MySerializeable { [JsonIgnore(Condition =JsonIgnoreCondition.Always)] public string Name = "dfdf";//被忽略 [JsonIgnore(Condition =JsonIgnoreCondition.Never)] public string Name2 = "dfdf"; [JsonIgnore(Condition =JsonIgnoreCondition.WhenWritingDefault)] public string Name3 = default;//将被忽略,因为使用默认值 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Name4 =null;//会被忽略因为null }
综合使用案例
using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.Unicode; JsonSerializerOptions jso = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, ReadCommentHandling = JsonCommentHandling.Skip, //跳过注释 AllowTrailingCommas = true,//忽略逗号 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,//属性名的格式,驼峰 NumberHandling = JsonNumberHandling.AllowReadingFromString,//反序列化的时候用的到 IncludeFields=true, //保证中文字符正确显示。CjkUnifiedIdeographs 象行文字 代表中文(Chinese)、日文(Japanese )、韩文(Korean)的字符集合 Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs),//从拉丁文字到象行文字 }; var json=JsonSerializer.SerializeToNode(new Myserliazer("祖海", "Summary", "Summary", "Summary"),jso); Console.WriteLine(json.ToJsonString(options: jso)); public class Myserliazer { public Myserliazer() { } [JsonConstructor] public Myserliazer(string name, string sumary, string description, string keyword) => (Name, Summary, Description, KeyWord) = (name, sumary, description, keyword); //[JsonInclude] 用法 [JsonInclude] public string Name = "董卿"; [JsonInclude]//若要允许使用非公共属性访问器,请使用 [JsonInclude] 特性 public string? Summary { private get; set; } = "Summary"; [JsonInclude] public string Content { get; private set; } = @"[OnSerialized()] internal void OnSerializedMethod(StreamingContext context) ... the call to the onSerializing method when the serialized objects are equal."; public string Frend = "哈哈"; //[JsonIgnore]用法 public string Description { get; set; } = "My first int"; [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public string KeyWord { get; set; } = "Web Site"; [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public DateTime DateTimenow { get; set; } = DateTime.Now; public string Eeditor{ get; } // [JsonExtensionData]用法 [JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; } = new Dictionary<string, JsonElement>(); // [JsonPropertyName]用法 [JsonPropertyName("Cangpr0pety")] public string ChangPr0pety { set; get; } = "ChangPr0pety"; public int Age { set; get; } = 12; public void OnDeserializing() { throw new NotImplementedException(); } }