.NET 6 中 System.Text.Json 的新特性

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET 6 中 System.Text.Json 的新特性相关的知识,希望对你有一定的参考价值。

1支持忽略循环引用

在 .NET 6 之前,如果 System.Text.Json 检测到循环引用,就会抛出 JsonException 异常。在 .NET 6 中,你可以忽略它们。

Category dotnet = new()

    Name = ".NET 6",
;
Category systemTextJson = new()

    Name = "System.Text.Json",
    Parent = dotnet
;
dotnet.Children.Add(systemTextJson);

JsonSerializerOptions options = new()

    ReferenceHandler = ReferenceHandler.IgnoreCycles,
    WriteIndented = true
;

string dotnetJson = JsonSerializer.Serialize(dotnet, options);
Console.WriteLine($"dotnetJson");

public class Category

    public string Name  get; set; 
    public Category Parent  get; set; 
    public List<Category> Children  get; set;  = new();


// Output:
// 
//   "Name": ".NET 6",
//   "Parent": null,
//   "Children": [
//     
//       "Name": "System.Text.Json",
//       "Parent": null,
//       "Children": []
//     
//   ]
// 

2(反)序列化事件通知

在 .NET 6 中,System.Text.Json 暴露了(反)序列化的事件通知接口。

有四个新的接口可以根据你的需要来实现:

  • IJsonOnDeserialized

  • IJsonOnDeserializing

  • IJsonOnSerialized

  • IJsonOnSerializing

Product invalidProduct = new()  Name = "Name", Test = "Test" ;
JsonSerializer.Serialize(invalidProduct);
// The InvalidOperationException is thrown

string invalidJson = "";
JsonSerializer.Deserialize<Product>(invalidJson);
// The InvalidOperationException is thrown

class Product : IJsonOnDeserialized, IJsonOnSerializing, IJsonOnSerialized

    public string Name  get; set; 

    public string Test  get; set; 

    public void OnSerialized()
    
        throw new NotImplementedException();
    

    void IJsonOnDeserialized.OnDeserialized() => Validate(); // Call after deserialization
    void IJsonOnSerializing.OnSerializing() => Validate();   // Call before serialization

    private void Validate()
    
        if (Name is null)
        
            throw new InvalidOperationException("The 'Name' property cannot be 'null'.");
        
    

3支持 Order 属性

以前,序列化顺序是由反射顺序决定的。在 .NET 6 中,System.Text.Json 添加了 JsonPropertyOrderAttribute 特性,它允许控制属性的序列化顺序。

Product product = new()

    Id = 1,
    Name = "Surface Pro 7",
    Price = 550,
    Category = "Laptops"
;

JsonSerializerOptions options = new()  WriteIndented = true ;
string json = JsonSerializer.Serialize(product, options);
Console.WriteLine(json);

class Product : A

    [JsonPropertyOrder(2)] // Serialize after Price
    public string Category  get; set; 

    [JsonPropertyOrder(1)] // Serialize after other properties that have default ordering
    public decimal Price  get; set; 

    public string Name  get; set;  // Has default ordering value of 0

    [JsonPropertyOrder(-1)] // Serialize before other properties that have default ordering
    public int Id  get; set; 


class A

    public int Test  get; set; 


// Output:
// 
//   "Id": 1,
//   "Name": "Surface Pro 7",
//   "Price": 550,
//   "Category": "Laptops"
// 

4用 Utf8JsonWriter 写原始 JSON

.NET 6 引入了 System.Text.Json.Utf8JsonWriter,用它可以写原始 JSON。

在你有下面需要的时候很有用:

  • 在新的 JSON 中包含现有的 JSON

  • 以不同于默认格式的方式对数值进行格式化

JsonWriterOptions writerOptions = new()  Indented = true, ;

using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream, writerOptions);

writer.WriteStartObject();
writer.WriteStartArray("customJsonFormatting");
foreach (double result in new double[]  10.2, 10 )

    writer.WriteStartObject();
    writer.WritePropertyName("value");
    writer.WriteRawValue(FormatNumberValue(result), skipInputValidation: true);
    writer.WriteEndObject();

writer.WriteEndArray();
writer.WriteEndObject();
writer.Flush();

string json = Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(json);

static string FormatNumberValue(double numberValue)

    return numberValue == Convert.ToInt32(numberValue)
        ? numberValue.ToString() + ".0"
        : numberValue.ToString();


// Output:
// 
//    "customJsonFormatting": [
//      
//        "value": 10.2
//      ,
//      
//        "value": 10.0
//      
//  ]
// 

5支持 IAsyncEnumerable

在 .NET 6 中,System.Text.Json 支持 IAsyncEnumerableIAsyncEnumerable 的序列化将其转化为一个数组。对于根级 JSON 数组的反序列化,增加了 DeserializeAsyncEnumerable 方法。

static async IAsyncEnumerable<int> GetNumbersAsync(int n)

    for (int i = 0; i < n; i++)
    
        await Task.Delay(1000);
        yield return i;
    

// Serialization using IAsyncEnumerable
JsonSerializerOptions options = new()  WriteIndented = true ;
using Stream outputStream = Console.OpenStandardOutput();
var data = new  Data = GetNumbersAsync(5) ;
await JsonSerializer.SerializeAsync(outputStream, data, options);
// Output:
// 
//    "Data": [
//      0,
//      1,
//      2,
//      3,
//      4
//  ]
// 

// Deserialization using IAsyncEnumerable
using MemoryStream memoryStream = new(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
// Wraps the UTF-8 encoded text into an IAsyncEnumerable<T> that can be used to deserialize root-level JSON arrays in a streaming manner.
await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(memoryStream))

    Console.WriteLine(item);

// Output:
// 0
// 1
// 2
// 3
// 4

下面的 GIF 展示了 IAsyncEnumerable 如何被序列化:

6JSON 和 Stream 互转

在 .NET 6 中,添加了同步方法 Serialize/DeserializeStream 重载。

string json = "\\"Value\\":\\"Deserialized from stream\\"";
byte[] bytes = Encoding.UTF8.GetBytes(json);

// Deserialize from stream
using MemoryStream ms = new MemoryStream(bytes);
Example desializedExample = JsonSerializer.Deserialize<Example>(ms);
Console.WriteLine(desializedExample.Value);
// Output: Deserialized from stream

// ==================================================================

// Serialize to stream
JsonSerializerOptions options = new()  WriteIndented = true ;
using Stream outputStream = Console.OpenStandardOutput();
Example exampleToSerialize = new()  Value = "Serialized from stream" ;
JsonSerializer.Serialize<Example>(outputStream, exampleToSerialize, options);
// Output:
// 
//    "Value": "Serialized from stream"
// 

class Example

    public string Value  get; set; 

7像 DOM 一样操作 JSON

在 .NET 6 中,提供了处理 in-memory 可写文档对象模型(DOM)的类型,用于随机访问结构化数据视图中的 JSON 元素。

新类型:

  • JsonArray

  • JsonNode

  • JsonObject

  • JsonValue

// Parse a JSON object
JsonNode jNode = JsonNode.Parse("\\"Value\\":\\"Text\\",\\"Array\\":[1,5,13,17,2]");
string value = (string)jNode["Value"];
Console.WriteLine(value); // Text
                          // or
value = jNode["Value"].GetValue<string>();
Console.WriteLine(value); // Text

int arrayItem = jNode["Array"][1].GetValue<int>();
Console.WriteLine(arrayItem); // 5
                              // or
arrayItem = jNode["Array"][1].GetValue<int>();
Console.WriteLine(arrayItem); // 5

// Create a new JsonObject
var jObject = new JsonObject

    ["Value"] = "Text",
    ["Array"] = new JsonArray(1, 5, 13, 17, 2)
;
Console.WriteLine(jObject["Value"].GetValue<string>());  // Text
Console.WriteLine(jObject["Array"][1].GetValue<int>());  // 5

// Converts the current instance to string in JSON format
string json = jObject.ToJsonString();
Console.WriteLine(json); // "Value":"Text","Array":[1,5,13,17,2]

8收尾

所有的代码示例你都可以在我的 GitHub 上找到:

https://github.com/okyrylchuk/dotnet6_features/tree/main/System.Text.Json%20features

原文:bit.ly/3zma38X
作者:Oleg Kyrylchuk
翻译:精致码农

以上是关于.NET 6 中 System.Text.Json 的新特性的主要内容,如果未能解决你的问题,请参考以下文章

.NET 6 新特性 System.Text.Json 中的 Writeable DOM

.NET 6 的 System.Text.Json 源代码生成不起作用

.NET 6新特性试用 | System.Text.Json序列化代码自动生成

json.net 到 System.text.json 对 .net 5 中嵌套类的期望

.NET Core/System.Text.Json:枚举和添加/替换 json 属性/值

是否有 System.Text.Json 替代 Json.NET 的 JsonProperty(Order)?