.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
支持 IAsyncEnumerable
。IAsyncEnumerable
的序列化将其转化为一个数组。对于根级 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
6 JSON 和 Stream 互转
在 .NET 6 中,添加了同步方法 Serialize
/Deserialize
的 Stream
重载。
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