.NET 8 Preview 1 中 SystemTextJson 的改进
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET 8 Preview 1 中 SystemTextJson 的改进相关的知识,希望对你有一定的参考价值。
.NET 8 Preview 1 中 SystemTextJson 的改进
Intro
System.Text.Json
是从 .NET Core 3.0 开始的一个新的 JSON 处理库,在之后的版本中一直在完善和改善性能,在 .NET 8 Preview 1 中完善一些支持,具体更新如下
Improvements
Unmapped Json Property Handling
在之前的版本中,如果 json 里 property 是不希望的内容不会有任何处理,在新版本中增加了没有 mapping 的 json property 处理,可以在找不到 mapping 的时候报错,示例如下:
file record Person(int Id, string Name);
var personJsonWithoutId = JsonSerializer.Serialize(new Id = 1, Name = "1234", Age = 10 );
try
var p = JsonSerializer.Deserialize<Person>(personJsonWithoutId);
Console.WriteLine(p?.ToString());
catch (Exception e)
Console.WriteLine(e);
不指定没有 mapping 的 JSON property 的时候默认是允许的,以上就会正常输出,不会走到 exception,输出如下:
Person Id = 1, Name = 1234
当我们指定了要报错的时候就会抛异常
try
var p = JsonSerializer.Deserialize<Person>(personJsonWithoutId,
new JsonSerializerOptions()
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow
);
Console.WriteLine(p?.ToString());
catch (Exception e)
Console.WriteLine(e);
输出结果如下:
System.Text.Json.JsonException: The JSON property 'Net8Sample.<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__Person' could not be mapped to any .NET member contained in type 'Age'.
at System.Text.Json.ThrowHelper.ThrowJsonException_UnmappedJsonProperty(Type type, String unmappedPropertyName)
at System.Text.Json.JsonSerializer.LookupProperty(Object obj, ReadOnlySpan`1 unescapedPropertyName, ReadStack& state, JsonSerializerOptions options, Boolean& useExtensionProperty, Boolean createExtensionProperty)
at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Utf8JsonReader& reader, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
at Net8Sample.JsonSample.MissingMemberHandlingTest()
除了指定 JsonSerializerOptions
我们也可以针对某一个类型添加 JsonUnmappedMemberHandling
标记,示例如下:
[JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Disallow)]
file record Person2
public required int Id get; init;
public required string Name get; init;
public string? JobTitle get; set;
try
var p = JsonSerializer.Deserialize<Person2>(personJsonWithoutId);
Console.WriteLine(p?.ToString());
catch (Exception e)
Console.WriteLine(e);
输出结果和前面的示例类似:
System.Text.Json.JsonException: The JSON property 'Net8Sample.<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__Person2' could not be mapped to any .NET member contained in type 'Age'.
at System.Text.Json.ThrowHelper.ThrowJsonException_UnmappedJsonProperty(Type type, String unmappedPropertyName)
at System.Text.Json.JsonSerializer.LookupProperty(Object obj, ReadOnlySpan`1 unescapedPropertyName, ReadStack& state, JsonSerializerOptions options, Boolean& useExtensionProperty, Boolean createExtensionProperty)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Utf8JsonReader& reader, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
at Net8Sample.JsonSample.MissingMemberHandlingTest()
在 System.Text.Json 中有个特殊的特性,我们可以使用 JsonExtensionData
来匹配那些没有 mapping 的 JSON property,那两个一起使用会不会报错呢,我们也来试一下
file record PersonWithExtensionData
public required int Id get; init;
public required string Name get; init;
[JsonExtensionData]
public Dictionary<string,object>? Extensions get; set;
try
var p = JsonSerializer.Deserialize<PersonWithExtensionData>(personJsonWithoutId,
new JsonSerializerOptions() UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow );
Console.WriteLine(JsonSerializer.Serialize(p));
catch (Exception e)
Console.WriteLine(e);
输出结果如下:
"Id":1,"Name":"1234","Age":10
是否和你猜测的一致呢
Interface Hierarchy
在之前的版本中如果我们使用接口进行序列化的话,接口继承的属性是不会被序列化的,比如下面的代码:
file interface IBase
int Base get; set;
file interface IDerived : IBase
int Derived get; set;
file class DerivedImplement : IDerived
public int Base get; set;
public int Derived get; set;
IDerived value = new DerivedImplement() Base = 0, Derived =1 ;
var serializedValue = JsonSerializer.Serialize(value);
Console.WriteLine(serializedValue);
在 .NET 7 中输出结果如下:
在 .NET 8 Preview 1 输出结果如下:
SnakeCaseNaming && KebabCaseNaming
在 .NET 8 Preview 1 中新增了两种属性名称序列化方式,SnakeCase 和 KebabCase,两种方式分别有 大写形式和小写形式,使用的时候在 JsonSerializerOptions
中指定 PropertyNamingPolicy
即可,我们直接看下示例吧
private static void SnakeCaseNamingTest()
var p = new Person2()
Id = 1,
Name = "Alice",
JobTitle = "Engineer"
;
var snakeCaseLowerJson = JsonSerializer.Serialize(p, new JsonSerializerOptions()
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
);
Console.WriteLine(snakeCaseLowerJson);
var snakeCaseUpperJson = JsonSerializer.Serialize(p, new JsonSerializerOptions()
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseUpper
);
Console.WriteLine(snakeCaseUpperJson);
输出结果如下:
再来看下 KebabCase 的示例:
private static void KebabCaseNamingTest()
var p = new Person2()
Id = 1,
Name = "Alice",
JobTitle = "Engineer"
;
var kebabCaseLowerJson = JsonSerializer.Serialize(p, new JsonSerializerOptions()
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower
);
Console.WriteLine(kebabCaseLowerJson);
var kebabCaseUpperJson = JsonSerializer.Serialize(p, new JsonSerializerOptions()
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseUpper
);
Console.WriteLine(kebabCaseUpperJson);
输出结果如下:
JsonSerializerOptions-ReadOnly
JsonSerializerOptions
中增加了 IsReadOnly
和 MakeReadOnly
两个方法,我们可以在为某个类型的序列化指定了某些序列化选项之后调用 MakeReadOnly
方法来保证序列化选项不会再被修改来保证序列化行为的一致性,下面是一个示例:
private static void JsonSerializerOptionsReadOnlyTest()
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
;
Console.WriteLine($"IsReadOnly: options.IsReadOnly");
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
Console.WriteLine("PropertyNamingPolicy updated");
options.MakeReadOnly();
Console.WriteLine($"IsReadOnly: options.IsReadOnly");
try
options.PropertyNamingPolicy = null;
catch (Exception e)
Console.WriteLine(e);
输出结果如下:
从上面的输出可以看得出来,在我们调用 MakeReadOnly
方法之前 IsReadOnly
会是 false
,是可以修改 options 的配置的,在调用之后 IsReadOnly
就变成 true
了,再修改 options 的配置就会抛异常
More
细心的小伙伴可能会发现第一个示例 Unmapped Json Property Handling
部分示例的异常信息是有点问题的,property 和 type 信息的位置反了,这是一个 BUG 。。,目前 bug 已经修复了,preview 2 应该就没这个问题了,修复 PR 可以参考:https://github.com/dotnet/runtime/pull/81718
References
https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-1/#json-improvements
https://github.com/dotnet/runtime/issues/37483
https://github.com/dotnet/runtime/pull/79945
https://github.com/dotnet/runtime/pull/78788
https://github.com/dotnet/runtime/pull/69613
https://github.com/dotnet/runtime/pull/74431
https://github.com/dotnet/runtime/pull/81718
https://github.com/WeihanLi/SamplesInPractice/blob/master/net8sample/Net8Sample/JsonSample.cs
以上是关于.NET 8 Preview 1 中 SystemTextJson 的改进的主要内容,如果未能解决你的问题,请参考以下文章
.NET 8 Preview 1 中 SystemTextJson 的改进
.NET 8 Preview 1 中新增的 Random 方法