可能有的小伙伴已经知道了,在.NET Core 3.0中微软加入了对JSON的内置支持。 一直以来.NET开发者们已经习惯使用Json.NET这个强大的库来处理JSON。 那么.NET为何要增长JSON的内置支持呢? 最近,.NET的官方博客再次发表文章说明了这么作的缘由、并介绍了相关API的用法。 现翻译出来分享给你们。 原文地址: https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
对于.NET Core 3.0,咱们 提供了一个名为System.Text.Json的全新命名空间 ,支持读取器/写入器,文档对象模型(DOM)和序列化。在这篇博文中,我告诉你为何咱们建造它,它是如何工做的,以及你如何试用它。git
咱们还有一个视频:github
https://sec.ch9.ms/ch9/f427/704ea54a-dac0-4ef8-b1bc-e9fde129f427/onNET_high.mp4web
JSON已经成为几乎全部现代.NET应用程序的重要组成部分,而且在许多状况下甚至超过了XML的使用范围。可是,.NET尚未(强大的)内置方式来处理JSON。相反,咱们一直依赖Json.NET,它继续为.NET生态系统提供良好的服务。json
咱们已经决定构建一个新的JSON库:后端
提供高性能的JSON API。咱们须要一组新的JSON API,这些API经过使用Span<T>
能够高度优化性能, 而且能够直接处理UTF-8而无需转码为UTF-16 string
实例。这两个方面对于ASP.NET Core相当重要,由于吞吐量是关键要求。咱们考虑过对Json.NET作出贡献,但若是不破坏现有的Json.NET客户或者破坏咱们能够实现的性能,这被认为是不可能的。有了这些 System.Text.Json
,咱们能够得到1.3倍速--5倍的速度,具体取决于场景(更多细节见下文)。咱们相信咱们仍然能够挤出更多。api
从ASP.NET Core中删除Json.NET依赖项。今天,ASP.NET Core依赖于Json.NET。虽然这提供了ASP.NET Core和Json.NET之间的紧密集成,但它也意味着Json.NET的版本由底层平台决定。可是,Json.NET常常更新,应用程序开发人员一般但愿 - 甚至必须 - 使用特定版本。所以,咱们但愿从ASP.NET Core 3.0中删除Json.NET依赖项,以便客户能够选择使用哪一个版本,而没必要担忧它们可能会意外破坏底层平台。服务器
为Json.NET提供ASP.NET Core集成包。Json.NET基本上已成为.NET中JSON处理的瑞士军刀。它提供了许多选项和工具,使客户能够轻松地处理其JSON需求。咱们不想在客户今天得到的Json.NET支持上妥协。例如,可以经过AddJsonOptions
扩展方法在ASP.NET Core中配置JSON序列化 。所以,咱们但愿为ASP.NET Core提供Json.NET集成做为开发人员能够选择安装的NuGet包,这样他们就能够得到今天从Json.NET得到的全部功能。此工做项的另外一部分是确保咱们拥有正确的扩展点,以便其余方能够为其选择的JSON库提供相似的集成包。mvc
有关动机及其与Json.NET的关系的更多详细信息,请查看 咱们在10月份发布的公告。app
对于全部示例,请确保导入如下两个名称空间:asp.net
using System.Text.Json; using System.Text.Json.Serialization;
该 System.Text.Json
序列化器能够异步读写JSON而且对UTF-8文本进行了优化,使其成为理想的REST API和后端应用程序。
class WeatherForecast { public DateTimeOffset Date { get; set; } public int TemperatureC { get; set; } public string Summary { get; set; } } string Serialize(WeatherForecast value) { return JsonSerializer.ToString<WeatherForecast>(value); }
默认状况下,咱们生成缩小的JSON。若是要生成易读的内容,能够将 JsonSerializerOptions
的实例传递给序列化程序。这也是您配置其余设置的方式,例如处理注释,尾随逗号和命名策略。
string SerializePrettyPrint(WeatherForecast value) { var options = new JsonSerializerOptions { WriteIndented = true }; return JsonSerializer.ToString<WeatherForecast>(value, options); }
反序列化的工做方式相似:
// { // "Date": "2013-01-20T00:00:00Z", // "TemperatureC": 42, // "Summary": "Typical summer in Seattle. Not.", // } WeatherForecast Deserialize(string json) { var options = new JsonSerializerOptions { AllowTrailingCommas = true }; return JsonSerializer.Parse<WeatherForecast>(json, options); }
咱们还支持异步序列化和反序列化:
async Task SerializeAsync(WeatherForecast value, Stream stream) { await JsonSerializer.WriteAsync<WeatherForecast>(value, stream); }
您还可使用自定义属性来控制序列化行为,例如,忽略属性并在JSON中指定属性的名称:
class WeatherForecast { public DateTimeOffset Date { get; set; } // 老是使用摄氏度 [JsonPropertyName("temp")] public int TemperatureC { get; set; } public string Summary { get; set; } // 不序列化这个属性 [JsonIgnore] public bool IsHot => TemperatureC >= 30; }
咱们目前不支持F#特定行为(例若有区别的联合和记录类型),但咱们 计划未来添加它。
有时您不但愿反序列化JSON有效负载,但您仍但愿对其内容进行结构化访问。例如,假设咱们有一组温度,并但愿计算出星期一的平均温度:
[ { "date": "2013-01-07T00:00:00Z", "temp": 23, }, { "date": "2013-01-08T00:00:00Z", "temp": 28, }, { "date": "2013-01-14T00:00:00Z", "temp": 8, }, ]
使用 JsonDocument 类能够很容易地访问各个属性和值。
double ComputeAverageTemperatures(string json) { var options = new JsonReaderOptions { AllowTrailingCommas = true }; using (JsonDocument document = JsonDocument.Parse(json, options)) { int sumOfAllTemperatures = 0; int count = 0; foreach (JsonElement element in document.RootElement.EnumerateArray()) { DateTimeOffset date = element.GetProperty("date").GetDateTimeOffset(); if (date.DayOfWeek == DayOfWeek.Monday) { int temp = element.GetProperty("temp").GetInt32(); sumOfAllTemperatures += temp; count++; } } var averageTemp = (double)sumOfAllTemperatures / count; return averageTemp; } }
写入器很容易使用:
var options = new JsonWriterOptions { Indented = true }; using (var stream = new MemoryStream()) { using (var writer = new Utf8JsonWriter(stream, options)) { writer.WriteStartObject(); writer.WriteString("date", DateTimeOffset.UtcNow); writer.WriteNumber("temp", 42); writer.WriteEndObject(); } string json = Encoding.UTF8.GetString(stream.ToArray()); Console.WriteLine(json); }
读取器须要在不一样的token类型间切换处理:
byte[] data = Encoding.UTF8.GetBytes(json); Utf8JsonReader reader = new Utf8JsonReader(data, isFinalBlock: true, state: default); while (reader.Read()) { Console.Write(reader.TokenType); switch (reader.TokenType) { case JsonTokenType.PropertyName: case JsonTokenType.String: { string text = reader.GetString(); Console.Write(" "); Console.Write(text); break; } case JsonTokenType.Number: { int value = reader.GetInt32(); Console.Write(" "); Console.Write(value); break; } // Other token types elided for brevity } Console.WriteLine(); }
在接受或返回对象有效负载时,经过自动序列化提供ASP.NET内核中大多数JSON的使用,这反过来意味着大多数应用程序的代码与ASP.NET Core正在使用的JSON库无关。这使得从一个切换到另外一个变得容易。
您能够在本文后面看到有关如何在MVC和SignalR中启用新JSON库的详细信息。
在预览版5中,ASP.NET Core MVC增长了对使用JSON读写的支持 System.Text.Json。从Preview 6开始,默认状况下使用新的JSON库来序列化和反序列化JSON有效负载。
可使用MvcOptions配置序列化程序的选项 :
services.AddControllers() .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
若是您想切换回以前的默认使用方式Newtonsoft.Json
,请执行如下操做:
在 ConfigureServices()
中加入AddNewtonsoftJson()
的调用
public void ConfigureServices(IServiceCollection services) { ... services.AddControllers() + .AddNewtonsoftJson() ... }
已知的问题
System.Text.Json
对OpenAPI / Swagger的支持正在进行,而且不太可能做为3.0版本的一部分提供。System.Text.Json
如今是SignalR客户端和服务器在ASP.NET Core 3.0 Preview 5中使用的默认集线协议(Hub Protocol)。
若是您想切换回之前的默认使用Newtonsoft.Json
,那么您能够在客户端和服务器上执行此操做。
Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson
NuGet包。在客户端 HubConnectionBuilder
中添加 .AddNewtonsoftJsonProtocol()
:
new HubConnectionBuilder() .WithUrl("/chatHub") .AddNewtonsoftJsonProtocol() .Build();
在服务器AddSignalR()
调用中添加 .AddNewtonsoftJsonProtocol()
:
services.AddSignalR() .AddNewtonsoftJsonProtocol();
因为此功能受性能的强烈推进,咱们但愿分享新API的一些高级性能特征。
请记住,这些都是基于预览版本,最终数字极可能会有所不一样。咱们还在调整会影响性能的默认行为(例如,区分大小写)。请注意,这些都是微基准测试。您的里程确定会有所不一样,所以若是性能对您相当重要,请确保针对最能表明您工做负载的方案进行本身的测量。若是您遇到但愿咱们进一步优化的方案,请提交错误。
只需进行微基准测试便可 System.Text.Json
与Json.NET 进行比较 ,得出如下结果:
场景 | 速度 | 内存 |
---|---|---|
反序列化 | 快2倍 | 持平或更低 |
序列化 | 快1.5倍 | 持平或更低 |
文件(只读) | 快3-5倍 | <1 MB无分配 |
读取器 | 快2-3倍 | 无分配(直到实现值(materialize values)) |
写入器 | 快1.3-1.6倍 | 无分配 |
咱们编写了一个ASP.NET Core应用程序,能够动态生成 数据 ,而后从MVC控制器进行序列化和反序列化 。而后咱们改变有效载荷大小并测量结果:
JSON反序列化(输入)
描述 | 吞吐量(RPS) | CPU (%) | 内存 (MB) |
---|---|---|---|
Newtonsoft.Json – 500 B | 136,435 | 95 | 172 |
System.Text.Json – 500 B | 167,861 | 94 | 169 |
Newtonsoft.Json – 2.4 KB | 97,137 | 97 | 174 |
System.Text.Json – 2.4 KB | 32,026 | 96 | 169 |
Newtonsoft.Json – 40 KB | 7,712 | 88 | 212 |
System.Text.Json – 40 KB | 16,625 | 96 | 193 |
JSON序列化(输出)
描述 | 吞吐量(RPS) | CPU(%) | 内存(MB) |
---|---|---|---|
Newtonsoft.Json - 500 B | 120,273 | 94 | 174 |
System.Text.Json - 500 B | 145,631 | 94 | 173 |
Newtonsoft.Json - 8 KB | 35,408 | 98 | 187 |
System.Text.Json - 8 KB | 56,424 | 97 | 184 |
Newtonsoft.Json - 40 KB | 8,416 | 99 | 202 |
System.Text.Json - 40 KB | 14,848 | 98 | 197 |
对于最多见的有效负载大小, System.Text.Json
在输入和输出格式化期间,MVC的吞吐量增长约20%,内存占用量更小。
在.NET Core 3.0中,咱们将发布新的 System.Text.Json
API,它们提供对JSON的内置支持,包括读取器/写入器,只读DOM和序列化/反序列化。主要目标是性能,咱们看到的通常速度比Json.NET高出2倍,但这取决于您的场景和有效负载,所以请确保衡量对您来讲重要的因素。
ASP.NET Core 3.0包含支持System.Text.Json
,默认状况下已启用。
试试 System.Text.Json
吧,而后反馈给咱们!
{"happy": "coding!"}