可能有的小夥伴已經知道了,在.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!"}