|
.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 的时候报错,示例如下:
filerecordPerson(intId,stringName);varpersonJsonWithoutId=JsonSerializer.Serialize(new{Id=1,Name=&#34;1234&#34;,Age=10});try{varp=JsonSerializer.Deserialize<Person>(personJsonWithoutId);Console.WriteLine(p?.ToString());}catch(Exceptione){Console.WriteLine(e);}不指定没有 mapping 的 JSON property 的时候默认是允许的,以上就会正常输出,不会走到 exception,输出如下:
Person{Id=1,Name=1234}当我们指定了要报错的时候就会抛异常
try{varp=JsonSerializer.Deserialize<Person>(personJsonWithoutId,newJsonSerializerOptions(){UnmappedMemberHandling=JsonUnmappedMemberHandling.Disallow});Console.WriteLine(p?.ToString());}catch(Exceptione){Console.WriteLine(e);}输出结果如下:
System.Text.Json.JsonException:TheJSONproperty&#39;Net8Sample.<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__Person&#39;couldnotbemappedtoany.NETmembercontainedintype&#39;Age&#39;.atSystem.Text.Json.ThrowHelper.ThrowJsonException_UnmappedJsonProperty(Typetype,StringunmappedPropertyName)atSystem.Text.Json.JsonSerializer.LookupProperty(Objectobj,ReadOnlySpan`1unescapedPropertyName,ReadStack&state,JsonSerializerOptionsoptions,Boolean&useExtensionProperty,BooleancreateExtensionProperty)atSystem.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.OnTryRead(Utf8JsonReader&reader,TypetypeToConvert,JsonSerializerOptionsoptions,ReadStack&state,T&value)atSystem.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader&reader,TypetypeToConvert,JsonSerializerOptionsoptions,ReadStack&state,T&value)atSystem.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&reader,JsonSerializerOptionsoptions,ReadStack&state)atSystem.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Utf8JsonReader&reader,ReadStack&state)atSystem.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1utf8Json,JsonTypeInfo`1jsonTypeInfo,Nullable`1actualByteCount)atSystem.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1json,JsonTypeInfo`1jsonTypeInfo)atSystem.Text.Json.JsonSerializer.Deserialize[TValue](Stringjson,JsonSerializerOptionsoptions)atNet8Sample.JsonSample.MissingMemberHandlingTest()除了指定 JsonSerializerOptions 我们也可以针对某一个类型添加 JsonUnmappedMemberHandling 标记,示例如下:
[JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Disallow)]filerecordPerson2{publicrequiredintId{get;init;}publicrequiredstringName{get;init;}publicstring?JobTitle{get;set;}}try{varp=JsonSerializer.Deserialize<Person2>(personJsonWithoutId);Console.WriteLine(p?.ToString());}catch(Exceptione){Console.WriteLine(e);}输出结果和前面的示例类似:
System.Text.Json.JsonException:TheJSONproperty&#39;Net8Sample.<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__Person2&#39;couldnotbemappedtoany.NETmembercontainedintype&#39;Age&#39;.atSystem.Text.Json.ThrowHelper.ThrowJsonException_UnmappedJsonProperty(Typetype,StringunmappedPropertyName)atSystem.Text.Json.JsonSerializer.LookupProperty(Objectobj,ReadOnlySpan`1unescapedPropertyName,ReadStack&state,JsonSerializerOptionsoptions,Boolean&useExtensionProperty,BooleancreateExtensionProperty)atSystem.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader&reader,TypetypeToConvert,JsonSerializerOptionsoptions,ReadStack&state,T&value)atSystem.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader&reader,TypetypeToConvert,JsonSerializerOptionsoptions,ReadStack&state,T&value)atSystem.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&reader,JsonSerializerOptionsoptions,ReadStack&state)atSystem.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Utf8JsonReader&reader,ReadStack&state)atSystem.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1utf8Json,JsonTypeInfo`1jsonTypeInfo,Nullable`1actualByteCount)atSystem.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1json,JsonTypeInfo`1jsonTypeInfo)atSystem.Text.Json.JsonSerializer.Deserialize[TValue](Stringjson,JsonSerializerOptionsoptions)atNet8Sample.JsonSample.MissingMemberHandlingTest()在 System.Text.Json 中有个特殊的特性,我们可以使用 JsonExtensionData 来匹配那些没有 mapping 的 JSON property,那两个一起使用会不会报错呢,我们也来试一下
filerecordPersonWithExtensionData{publicrequiredintId{get;init;}publicrequiredstringName{get;init;}[JsonExtensionData]publicDictionary<string,object>?Extensions{get;set;}}try{varp=JsonSerializer.Deserialize<PersonWithExtensionData>(personJsonWithoutId,newJsonSerializerOptions(){UnmappedMemberHandling=JsonUnmappedMemberHandling.Disallow});Console.WriteLine(JsonSerializer.Serialize(p));}catch(Exceptione){Console.WriteLine(e);}输出结果如下:
{&#34;Id&#34;:1,&#34;Name&#34;:&#34;1234&#34;,&#34;Age&#34;:10}是否和你猜测的一致呢
Interface Hierarchy
在之前的版本中如果我们使用接口进行序列化的话,接口继承的属性是不会被序列化的,比如下面的代码:
fileinterfaceIBase{intBase{get;set;}}fileinterfaceIDerived:IBase{intDerived{get;set;}}fileclassDerivedImplement:IDerived{publicintBase{get;set;}publicintDerived{get;set;}}IDerivedvalue=newDerivedImplement(){Base=0,Derived=1};varserializedValue=JsonSerializer.Serialize(value);Console.WriteLine(serializedValue);在 .NET 7 中输出结果如下:

.NET 7 interface serialize output在 .NET 8 Preview 1 输出结果如下:

.NET 8 Preview interface serialize outputSnakeCaseNaming && KebabCaseNaming
在 .NET 8 Preview 1 中新增了两种属性名称序列化方式,SnakeCase 和 KebabCase,两种方式分别有 大写形式和小写形式,使用的时候在 JsonSerializerOptions 中指定 PropertyNamingPolicy 即可,我们直接看下示例吧
privatestaticvoidSnakeCaseNamingTest(){varp=newPerson2(){Id=1,Name=&#34;Alice&#34;,JobTitle=&#34;Engineer&#34;};varsnakeCaseLowerJson=JsonSerializer.Serialize(p,newJsonSerializerOptions(){PropertyNamingPolicy=JsonNamingPolicy.SnakeCaseLower});Console.WriteLine(snakeCaseLowerJson);varsnakeCaseUpperJson=JsonSerializer.Serialize(p,newJsonSerializerOptions(){PropertyNamingPolicy=JsonNamingPolicy.SnakeCaseUpper});Console.WriteLine(snakeCaseUpperJson);}输出结果如下:

SnakeCaseNaming output再来看下 KebabCase 的示例:
privatestaticvoidKebabCaseNamingTest(){varp=newPerson2(){Id=1,Name=&#34;Alice&#34;,JobTitle=&#34;Engineer&#34;};varkebabCaseLowerJson=JsonSerializer.Serialize(p,newJsonSerializerOptions(){PropertyNamingPolicy=JsonNamingPolicy.KebabCaseLower});Console.WriteLine(kebabCaseLowerJson);varkebabCaseUpperJson=JsonSerializer.Serialize(p,newJsonSerializerOptions(){PropertyNamingPolicy=JsonNamingPolicy.KebabCaseUpper});Console.WriteLine(kebabCaseUpperJson);}输出结果如下:

KebabCaseNaming outputJsonSerializerOptions-ReadOnly
JsonSerializerOptions 中增加了 IsReadOnly 和 MakeReadOnly 两个方法,我们可以在为某个类型的序列化指定了某些序列化选项之后调用 MakeReadOnly 方法来保证序列化选项不会再被修改来保证序列化行为的一致性,下面是一个示例:
privatestaticvoidJsonSerializerOptionsReadOnlyTest(){varoptions=newJsonSerializerOptions(JsonSerializerDefaults.Web){TypeInfoResolver=newDefaultJsonTypeInfoResolver()};Console.WriteLine($&#34;IsReadOnly:{options.IsReadOnly}&#34;);options.PropertyNamingPolicy=JsonNamingPolicy.CamelCase;Console.WriteLine(&#34;PropertyNamingPolicyupdated&#34;);options.MakeReadOnly();Console.WriteLine($&#34;IsReadOnly:{options.IsReadOnly}&#34;);try{options.PropertyNamingPolicy=null;}catch(Exceptione){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
-
技术群:添加小编微信并备注进群
小编微信:mm1552923
公众号:dotNet编程大全
|
|