https://www.zhihu.com/question/449756804
作者:醉书生
链接:https://www.zhihu.com/question/449756804/answer/2219422015
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
系统.文本.Json
System.Text.Json
提供了多种高性能的 API 来处理 JSON 文档。在过去的几个版本中,我们添加了新功能,以进一步提高 JSON 处理性能并减轻想要从. 此版本包括在这条道路上的继续,并且是性能的重大进步,特别是在序列化器源生成器方面。NewtonSoft.Json
JsonSerializer
源代码生成
注意:应重新编译使用 .NET 6 RC1 或更早版本的源代码生成的应用程序。
几乎所有 .NET 序列化程序的支柱都是反射。反射对于某些场景来说是一项很棒的功能,但不能作为高性能云原生应用程序(通常(反)序列化和处理大量 JSON 文档)的基础。反射是启动、内存使用和程序集修整的问题。
运行时反射的替代方案是编译时源代码生成。在 .NET 6 中,我们将一个"https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/">新的源生成器作为System.Text.Json. JSON 源生成器可与JsonSerializer
多种方式结合使用并可进行配置。
它可以提供以下好处:
- 减少启动时间
- 提高序列化吞吐量
- 减少私有内存使用
- 删除运行时使用和
System.ReflectionSystem.Reflection.Emit
- IL 修整兼容性
默认情况下,JSON 源生成器为给定的可序列化类型发出序列化逻辑。JsonSerializer
通过生成Utf8JsonWriter
直接使用的源代码,这提供了比使用现有方法更高的性能。简而言之,源代码生成器提供了一种在编译时为您提供不同实现的方法,以便使运行时体验更好。
给定一个简单类型:
namespace Test
{
internal class JsonMessage
{
public string Message { get; set; }
}
}
源生成器可以配置为为示例JsonMessage
类型的实例生成序列化逻辑。请注意,类名JsonContext
是任意的。您可以为生成的源使用任何您想要的类名。
using System.Text.Json.Serialization;
namespace Test
{
[JsonSerializable(typeof(JsonMessage)]
internal partial class JsonContext : JsonSerializerContext
{
}
}
使用此模式的序列化程序调用可能类似于以下示例。此示例提供了可能的最佳性能。
using MemoryStream ms = new();
using Utf8JsonWriter writer = new(ms);
JsonSerializer.Serialize(jsonMessage, JsonContext.Default.JsonMessage);
writer.Flush();
// Writer contains:
// {"Message":"Hello, world!"}
最快和最优化的源代码生成模式——基于Utf8JsonWriter
——目前仅可用于序列化。Utf8JsonReader
根据您的反馈,未来可能会提供类似的反序列化支持——基于——。
源生成器还发出类型元数据初始化逻辑,这也有利于反序列化。要反序列化JsonMessage
使用预生成类型元数据的实例,您可以执行以下操作:
链接:https://www.zhihu.com/question/449756804/answer/2219422015
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);
JsonSerializer
支持 IAsyncEnumerable
现在,可以(反)序列化JSON阵列IAsyncEnumerable<T>与。以下示例使用流作为数据的任何异步源的表示。源可以是本地机器上的文件,也可以是数据库查询或 Web 服务 API 调用的结果。System.Text.Json
JsonSerializer.SerializeAsync
已更新以识别IAsyncEnumerable
值并提供特殊处理。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
static async IAsyncEnumerable<int> PrintNumbers(int n)
{
for (int i = 0; i < n; i++) yield return i;
}
using Stream stream = Console.OpenStandardOutput();
var data = new { Data = PrintNumbers(3) };
await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}
IAsyncEnumerable
值仅支持使用异步序列化方法。尝试使用同步方法进行序列化将导致NotSupportedException
抛出异常。
流式反序列化需要一个新的 API 返回. 我们为此目的添加了该方法,您可以在以下示例中看到。IAsyncEnumerable<T>JsonSerializer.DeserializeAsyncEnumerable
using System;
using System.IO;
using System.Text;
using System.Text.Json;
var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(stream))
{
Console.WriteLine(item);
}
此示例将按需反序列化元素,并且在使用特别大的数据流时非常有用。它只支持从根级 JSON 数组中读取,尽管将来可能会根据反馈放宽。
现有DeserializeAsync
方法名义上支持,但在其非流式方法签名的范围内。它必须将最终结果作为单个值返回,如下例所示。IAsyncEnumerable<T>
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));
var result = await JsonSerializer.DeserializeAsync<MyPoco>(stream);
await foreach (int item in result.Data)
{
Console.WriteLine(item);
}
public class MyPoco
{
public IAsyncEnumerable<int> Data { get; set; }
}
在此示例中,反序列化器将IAsyncEnumerable
在返回反序列化对象之前缓冲内存中的所有内容。这是因为反序列化器需要在返回结果之前消耗整个 JSON 值。
链接:https://www.zhihu.com/question/449756804/answer/2219422015
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
System.Text.Json:可写 DOM 功能
在写JSON DOM功能增加了一个新的简单的,高性能的编程模型的。这个新的 API 很有吸引力,因为它避免了需要强类型的序列化契约,并且与现有类型相比,DOM 是可变的。System.Text.JsonJsonDocument
这个新的 API 有以下好处:
- 在不可能或不希望使用POCO类型的情况下,或者当 JSON 模式不固定且必须检查时,序列化的轻量级替代方案。
- 允许对大树的子集进行有效修改。例如,可以高效地导航到大型 JSON 树的子部分并从该子部分读取数组或反序列化 POCO。LINQ 也可以与它一起使用。
以下示例演示了新的编程模型。
// Parse a JSON object
JsonNode jNode = JsonNode.Parse("{"MyProperty":42}");
int value = (int)jNode["MyProperty"];
Debug.Assert(value == 42);
// or
value = jNode["MyProperty"].GetValue<int>();
Debug.Assert(value == 42);
// Parse a JSON array
jNode = JsonNode.Parse("[10,11,12]");
value = (int)jNode[1];
Debug.Assert(value == 11);
// or
value = jNode[1].GetValue<int>();
Debug.Assert(value == 11);
// Create a new JsonObject using object initializers and array params
var jObject = new JsonObject
{
["MyChildObject"] = new JsonObject
{
["MyProperty"] = "Hello",
["MyArray"] = new JsonArray(10, 11, 12)
}
};
// Obtain the JSON from the new JsonObject
string json = jObject.ToJsonString();
Console.WriteLine(json); // {"MyChildObject":{"MyProperty":"Hello","MyArray":[10,11,12]}}
// Indexers for property names and array elements are supported and can be chained
Debug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue<int>() == 11);
ReferenceHandler.IgnoreCycles
JsonSerializer(System.Text.Json)
现在支持在序列化对象图时忽略循环的能力。该选项的行为与 Newtonsoft.Json 相似。一个主要区别是 System.Text.Json 实现用JSON 标记替换引用循环,而不是忽略对象引用。ReferenceHandler.IgnoreCyclesReferenceLoopHandling.Ignorenull
您可以在以下示例中看到 的行为。在这种情况下,属性被序列化,因为它否则会创建一个循环。ReferenceHandler.IgnoreCyclesNextnull
class Node
{
public string Description { get; set; }
public object Next { get; set; }
}
void Test()
{
var node = new Node { Description = "Node 1" };
node.Next = node;
var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };
string json = JsonSerializer.Serialize(node, opts);
Console.WriteLine(json); // Prints {"Description":"Node 1","Next":null}
}
作者:醉书生
链接:https://www.zhihu.com/question/449756804/answer/2219422015
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。