JSON序列化: System.Text.Json与Newtonsoft.Json
两者的比较
System.Text.Json
: 官方, 高性能, 低内存, 默认行为严格, 相对不灵活, 支持源生成, AOT
Newtonsoft.Json
: 第三方, 性能较高, 功能丰富, 默认行为相对宽松, 强大的灵活性, 不支持源生成, 不能AOT
System.Text.Json
迁移至Newtonsoft.Json
, 简单
Newtonsoft.Json
迁移至System.Text.Json
, 复杂, 要改很多东西(如果json严格,数据结构简单就相对好很多), 扩展阅读中查看
System.Text.Json 基本使用
序列化 JsonSerializer.Serialize
注意, 序列化的默认行为, 这是学习序列化的关键一步:
- 默认情况下,所有
公共(public)
的属性(Property)
都会序列化, 会忽略字段
。 - 默认无空格,不对齐,区分大小写
// 注意: 这里默认一定是 public 的 属性, 否则会失败
public class Weather
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
var weather = new Weather {Date = DateTime.Parse("2019-08-01"), TemperatureCelsius = 25, Summary = "Hot"};
string jsonString1 = JsonSerializer.Serialize(weather);
// 注意: 官方提供了很多种序列化/反序列化方法
// 包括: 泛型/非泛型(object)/源生成
public static string Serialize<TValue>(TValue value, JsonSerializerOptions? options = null); // 泛型
public static string Serialize(object? value, Type inputType, JsonSerializerOptions? options = null); // object
public static string Serialize<TValue>(TValue value, JsonTypeInfo<TValue> jsonTypeInfo); // 泛型,源生成
public static string Serialize(object? value, JsonTypeInfo jsonTypeInfo); // object,源生成
public static string Serialize(object? value, Type inputType, JsonSerializerContext context); // object,源生成
// 另外还包括 Stream流, Task异步, 很多个方法, 这里就不一一列举了
public static void Serialize<TValue>(Stream utf8Json, TValue value, JsonSerializerOptions? options = null)
public static Task SerializeAsync(Stream utf8Json, object? value, Type inputType, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
......
反序列化
注意, 反序列化默认行为, 关键
- 区分大小写
- 枚举作为数字
- 忽略字段⭐
- JSON 中的注释或尾随逗号会引发异常
- 最大深度为 64
var jsonString = """{"Date":"2019-08-01T00:00:00-07:00","TemperatureCelsius":25,"Summary":"Hot"}""";
Weather? weatherForecast = JsonSerializer.Deserialize<Weather>(jsonString);
// 当然, 反序列化也有很多种方法
// 包括: 泛型/非泛型(object)/源生成, 这里还支持 ReadOnlySpan<char>参数
public static TValue? Deserialize<TValue>(string json, JsonSerializerOptions? options = null);
public static TValue? Deserialize<TValue>(ReadOnlySpan<char> json, JsonSerializerOptions? options = null)
public static object? Deserialize(string json, Type returnType, JsonSerializerOptions? options = null)
public static object? Deserialize(ReadOnlySpan<char> json, Type returnType, JsonSerializerOptions? options = null)
public static TValue? Deserialize<TValue>(string json, JsonTypeInfo<TValue> jsonTypeInfo)
......
// 另外还包括 Stream流, Task异步, 很多个方法, 这里就不一一列举了
(略)
// required 修饰符 或 [JsonRequired] 表示json中必须包含 c#11
public required string Name { get; set; }
[JsonRequired]
public string Name { get; set; }
定制JSON序列化行为
// 常用选项
var options = new JsonSerializerOptions
{
WriteIndented = true, // 缩进
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // 默认忽略null值
PropertyNameCaseInsensitive = true, // 如果需要忽略大小写
IncludeFields = true // 是否处理Fields
}
// 定制JSON序列化行为, 列出了主要的属性[详细见扩展阅读]
// JsonSerializerOptions 类
// JsonSourceGenerationOptionsAttribute 类
AllowTrailingCommas; // ⭐JSON值列表末尾是否有额外的逗号
Converters; // ⭐自定义转换器列表, 例如: 枚举类型转换, 日期时间转化等
DefaultBufferSize; // 创建临时缓冲区时使用的默认缓冲区大小(以字节为单位)
DefaultIgnoreCondition; // ⭐何时忽略具有默认值的属性。 默认值为 Never, 可选: Always(总是),Never(从不),WhenWritingDefault(等于默认值时),WhenWritingNull(为null时)
DictionaryKeyPolicy; // 用于设置将字典的键名称转化成另外一种格式的策略
IgnoreReadOnlyFields; // 是否忽略只读(readonly)字段, 默认false
IgnoreReadOnlyProperties; // 是否忽略只读(readonly)属性, 默认false
IncludeFields; // ⭐是否处理字段。 默认false, 注意默认是不处理字段的
MaxDepth; // JSON 时允许的最大深度,默认值为 0,表示最大深度为 64。
NumberHandling; // ⭐设置一个对象, 如何处理数字类型, 例如数字字符串
PreferredObjectCreationHandling; // 首选对象创建处理
PropertyNameCaseInsensitive; // ⭐反序列化时是否使用不区分大小写
PropertyNamingPolicy; // 将对象上的属性名称转换为其他格式的策略,如大小写转化等
ReadCommentHandling; // ⭐反序列化期间如何处理注释
UnknownTypeHandling; // 反序列化期间如何处理声明为 Object 的类型。JsonElement或JsonNode
UnmappedMemberHandling; // 反序列化对象类型时如何处理无法映射到特定实例成员的 JSON 属性。默认忽略0,可以设置异常1
WriteIndented; // ⭐JSON是否缩进,空格等, 默认是无缩进和空格的, 默认为false
GenerationMode; //
UseStringEnumConverter; // ⭐字符串枚举, 默认false
System.Text.Json 中使用 JSON 文档对象模型
在没有要反序列化到的类型
或收到的 JSON 没有固定架构
时, 我们可以使用JSON 文档对象模型 (DOM)
进行反序列化后可随机访问其内的数据。这时可以将josn反序列化为 JsonElement(只读/访问快)
或 JsonNode(读写)
。任何有效的 JSON 属性都可以反序列化为 JsonElement 或 JsonNode。
// JsonNode 基本操作
var jsonString = """......""";
// 反序列化
JsonNode forecastNode = JsonNode.Parse(jsonString)!;
// 访问值
JsonNode temperatureNode = forecastNode!["Temperature"]!;
int temperatureInt = forecastNode!["Temperature"]!.GetValue<int>();
// 修改值
forecastNode!["Temperature"] = 111;
// JsonElement 基本操作
double sum = 0;
int count = 0;
using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
JsonElement studentsElement = root.GetProperty("Students");
foreach (JsonElement student in studentsElement.EnumerateArray())
{
if (student.TryGetProperty("Grade", out JsonElement gradeElement))
{
sum += gradeElement.GetDouble();
}
else
{
sum += 70;
}
count++;
}
}
序列化遇到的坑
运行原理
默认情况下,System.Text.Json
使用反射来收集所需的元数据,用于在运行时访问对象属性以进行序列化和反序列化。
System.Text.Json
可以使用 C# 源生成
功能来提高性能、降低专用内存使用量以及推动程序集修整,从而缩小应用大小。
Byte[]序列化错误
System.Text.Json
不提供 byte[]
类型的数据直接处理 [1,2,3,4]
这种类型. 而是直接提供 Base64字符串
形式.
$type在JSON中读取类型信息
System.Text.Json
中排除了 TypeNameHandling.All
的等效功能。但其提供了多态反序列化
, 无法直接处理$type
动态类型.
访问修饰符问题
System.Text.Json
中默认的解析基本都是public
, 属性
. 需要进行默认行为设置. 但是我在AOT时, 发现初始化类型时, 某些无参数公共方法必须为public
System.Text.Json 源生成器
Newtonsoft.Json 基本使用
扩展阅读
微软官方: 内置转换器源代码
.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json
从Newtonsoft.Json迁移到 System.Text.Json不简单
微软官方: 从 Newtonsoft.Json 迁移到 System.Text.Json
如何在 System.Text.Json 中使用源生成
微软官方: JsonSerializerOptions 类
微软官方: JsonSourceGenerationOptionsAttribute 类
多态反序列化JsonConverter
最后更新于 2024-10-10 00:10:26 并被添加「」标签,已有 519 位童鞋阅读过。
本站使用「署名 4.0 国际」创作共享协议,可自由转载、引用,但需署名作者且注明文章出处
此处评论已关闭