.NET Core 3.0 裏新的JSON API

爲何須要新的JSON API 

JSON.NET 你們都用過,老版本的ASP.NET Core也依賴於JSON.NET json

然而這個依賴就會引發一些版本問題:例如ASP.NET Core某個版本須要使用JSON.NET v10,而另外一個庫須要使用JSON.NET v11;或者JSON.NET 出現了一個新版本,而ASP.NET Core還不能支持這個版本,而您卻想使用該版本。 數組

 

System.Text.Json 

隨着NET Core 3.0的出現,出現了System.Text.Json命名空間和它下面一些用於處理JSON的類。 函數

 

特色

這個內置JSON API具備與生俱來的高性能、低分配的特色: post

JSON.NET 使用.NET 裏面的字符串做爲基本數據類型,其實也就是UTF16,而.NET Core中新的JSON API直接使用數據原始的UTF8格式。 性能

新的JSON API基於Span<byte>這個數據類型來進行操做JSON數據,從而具備低分配的特色,這就能夠極大的改善吞吐量和內存使用狀況。 ui

 

可是新的JSON API的特性還不那麼豐富,有一些JSON.NET具備的特性都還不支持。 spa

 

例子 

隨便找了一個JSON示例文件: code

"post Title": "Programming" , 
"language": 
"author": 
"firstName": "Nick", 
"lastName": "Carter" 
"publishedAt 
"wordCount " : 
13435, 
"isoriginal"• 
true, 
"tags" • 
• "C#", "JSON API", ".NET Core" ]

 

針對這個文件,須要修改一下它的屬性: orm

sample.json File P 「 operties 
Advanced 
Custom T00 一 Namespace 
Custom T00- 
Copy to Output 「 to 
Build Action 
〔 を

 

Utf8JsonReader 

先使用 Utf8JsonReader 來讀取JSON文件。 xml

Utf8JsonReader 並不會讀取文件或者stream,它會讀取Span數據類型。 

 

直接上代碼: 

static void Elgin(stringC] 
gx•gs 
File . ReadAllBytes( path: "sample.json" ); 
var dataBytes 
dataBytes .AsSpan( ) ; 
var jsonSpan 
new Utf8JsonReader(jsonSpan); 
var jsonReader = 
while (jsonReader.Read()) 
Console . jsonReader)); 
I reference 
private static string GetTokenInfo(Utf8JsonReader jsonReader) 
jsonReader . TokenType switch 
JsonTokenType.StartObject 
"START OBJECT" , 
JsonTokenType 
. EndObject "END OBJECT" , 
JsonTokenType.StartArray 
"START ARRAY" , 
JsonTokenType. EndArray "End ARRAY" , 
JsonTokenType. PropertyName $ " PROPERTY: {jsonReader.GetString()}" , 
$"COMMENT: {jsonReader.GetString()}" , 
JsonTokenType 
. Comment 
JsonTokenType.String $"STRING: {jsonReader.GetString()}" , 
$"NUMBER: {jsonReader.GetInt32()}" , 
JsonTokenType.Number 
$"BOOL: {jsonReader.GetBoolean()}" , 
JsonTokenType 
. True 
$"BOOL: {jsonReader.GetBoolean()}" , 
JsonTokenType 
JsonTokenType.Null 
"NULL" , 
$ TOKEN: {jsonReader.TokenType}"

Main方法裏面,咱們使用File.ReadAllBytessample.json文件讀取數格式爲byte[],而後經過AsSpan這個擴展方法將其轉化爲Span<byte>數據類型,而後把它傳遞到 Utf8JsonReader 的構造函數來建立一個JSON的reader 

 

接下來使用while循環對JSON數據的每一個Token進行讀取,每次執行Read()方法時,reader就會移動到JSON數據裏面的下一個Token那裏。 

 

Token分紅幾種類型,GetTokenInfo方法就是判斷一下Token的類型,並返回一些描述性信息,這裏面應該是包含了全部的類型。這裏面使用到了C# 8  switch 表達式。 

 

運行程序 

結果以下: 

hema Selected> 
"post Title": "Programming" , 
"language": 
"author": 
"lastName": "Carter" 
"firstName": 
"publishedAt 
"Nick", 
"wordCount " : 
13435, 
"isoriginal"• 
true, 
"tags" • 
• "C#", "JSON 
API", ".NET Core" 
PROPERTY: postTit1e 
STRING: Programming 
PROPERTY: language 
STRING: 
PROPERTY: author 
START OBJECT 
PROPERTY: firstName 
STRING: Nick 
PROPERTY: lastName 
STRING: carter 
END OBJECT 
PROPERTY: publishedAt 
STRING: 
PROPERTY: wordcount 
NUMBER: 13435 
PROPERTY: isoriginal 
BOOL: True 
PROPERTY: tags 
START ARRAY 
STRING: 
STRING: JSON API 
STRING: .NET core 
End ARRAY 
END OBJECT

能夠看到sample.json文件裏面的每一個Token都被正確的顯示了。 

 

JsonDocument 

JsonDocument是基於Utf8JsonReader 構建的JsonDocument 可分析 JSON 數據並生成只讀文檔對象模型 (DOM),可對模型進行查詢,以支持隨機訪問和枚舉。使用 JsonDocument 分析常規 JSON 有效負載並訪問其全部成員比使用 Json.NET  2-3 倍,且爲合理大小(即 < 1 MB)的數據所分配的量很是少。 

JsonDocument能夠處理Span,也能夠處理Stream 

 

例子: 

using 
using 
var 
var 
File .0penRead( path: 
"sample.json"); 
stream = 
JsonDocument . Parse( stream) ; 
doc

這裏我經過File.OpenReadjson文件轉化爲stream。而後使用JsonDocument.Parse方法把stream解析成JSON文檔對象模型。 

注意,這裏我使用了C# 8using var語法,這個之後再說。 

 

下面咱們開始從這個JSON文檔對象模型的根節點開始遍歷,也就是RootElement 

root 
var 
doc . Root Element;

 

而後經過root這個JsonElement類型的對象的GetProperty方法來得到相應的屬性,並且這個方法能夠連串使用: 

firstName 
var 
root .GetProperty( "author") 
. GetProperty( "firstName" ) 
. GetString( ) ;

最後一行使用GetString方法來得到該屬性的字符串值。 

 

而後咱們能夠寫一個遞歸調用的方法來遍歷整個模型的每一個屬性: 

EnumerateElement(JsonElement root) 
private static void 
foreach (var prop in root. EnumerateObject()) 
if (prop.Value.ValueKind = 
JsonValueKind .0bject) 
Console . WriteLine($" {prop . Name}" ) ; 
Console.WriteLine(" 
- Object Start - 
EnumerateElement(prop . Value); 
Console.WriteLine(" 
Object End 
else 
{prop. Value.GetRawText()}");

這個方法接受JsonElement類型的對象,而後對該元素的屬性進行循環。 

若是當前屬性是另外一個對象,那麼就繼續遞歸調用這個方法; 

不然就輸出原始的文本。 

 

最後調用該方法: 

File. OpenRead( 
"sample.json"); 
using var stream 
path: 
JsonDocument . Parse( stream) ; 
using var doc - 
doc . Root Element; 
var root 
var firstName 
root .GetProperty( "author") 
. GetProperty( "firstName" ) 
. GetString( ) 
Console.WriteLine($"First Name is {firstName}"); 
EnumerateElement(root);

 

輸出結果爲: 

First Name 
post Title: 
language: 
author 
is Nick 
" Programmi ng" 
Object Start 
irstName: "Nick" 
lastName: "Carter" 
Object End 
publishedAt : 
22T2ø: 21 . eeøz" 
rdcount: 13435 
isoriginal: true 
ags: [ "C#", " 
JSON API 
% ".NET core" :

與json文件的內容匹配。 

 

Utf8JsonWriter 

下面研究一下如何寫入json文件。這裏須要使用Utf8JsonWriter類。 

直接看代碼: 

這個類須要傳遞的參數類型是Stream或者Buffer,也就是向StreamBuffer裏面寫入數據。 

 

那麼就提供一個buffer 

var buffer = 
new 
= new Utf8JsonWriter(buffer); 
using var 
'son

 

下面單獨寫一個方法,來生成json數據: 

PopulateJson(Utf8JsonWriter json) 
private static void 
Json. 
write 
W rite 
W r ite Base64St ringVaIue 
W r ite Boolean 
W r ite BooleanVaIue 
W r iteCo m mentVaIue 
Write EndArray 
W r ite EndObject 
Wr iteNuII 
W r ite NullVaIue 
Write Number 
W rite Nu mbe rVaIue 
void 
void 
void 
void 
void 
void 
void 
void 
void 
void 
void 
(JsonEncodedText propertyName, ReadOnIyS 
Writes the pre-encoded property name and 
a name/value pair of a JSON object. 
utf8PropertyName, Re; 
propertyName, ReadO 
(string propertyName,

參數類型是Utf8JsonWriter。經過智能提示能夠看到它提供了不少用於寫入不一樣類型數據的方法。 

 

JSON對象 

如今我想寫一個json對象,那麼就從WriteStartObject()開始,而後以WriteEndObject()結束: 

json . WriteStartObject( ); 
json.WriteEndObject();

這樣的話,實際上我已經擁有了一個合法的json文檔。 

 

寫屬性和值 

能夠分開寫屬性和值: 

json . WritePropertyName( "title"); 
json . WriteStringValue( " Programming" ) ;

 

也能夠同時把屬性和值寫出來: 

json .WriteString( propertyName: "language" , 
"C#"); 
value:

 

顯示JSON數據 

我先寫這些內容,而後在Main方法裏面調用一下: 

grgs) 
static void Mgin(stringC] 
new ArrayBufferWriter<byte>(); 
var buffer - 
new Utf8JsonWriter(buffer); 
using var Ison = 
PopulateJson( j son) ; 
I reference 
private static void PopulateJson(Utf8JsonWriter json) 
Json. 
Json. 
Json. 
Json. 
Json. 
Json. 
Json. 
WriteStartObject( ) ; 
WritePropertyName( "title"); 
WriteStringValue( " Programming" ) ; 
WriteString( propertyName: "language" , 
WriteString( propertyName: "firstName" , 
WriteString( propertyName: "lastName" , 
WriteEndObject( ) ; 
"C#"); 
value: 
"Nick"); 
value: 
"Carter"); 
value:

 

首先須要告訴writer把它的內容flush給buffer,使用這個buffer咱們能夠得到writer的輸出,這樣的話就會獲得一個byte數組,而後把這個byte數組轉化爲字符串,這樣就能夠在控制檯顯示它了: 

new ArrayBufferWriter<byte>(); 
var buffer = 
new Utf8JsonWriter(buffer); 
using var Ison = 
PopulateJson( json); 
json. Flush(); 
var output = buffer.WrittenSpan.ToArray(); 
Encoding. UTF8. GetString(output); 
var outJson - 
Console. WriteLine(outJson);

 

運行一下看看效果: 

{"title" : "Programming" , "language" : "C#" , "firstName" : "Nick" , "lastName" : "Carter"}

沒啥太大的問題,就是格式很差看。 

 

對輸出進行格式化 

.NET Core提供了一個JsonWriterOptions類,它能夠對Writer進行一些設置。 

var options = 
Indented = 
new JsonWriterOptions 
true 
new Utf8JsonWriter(buffer, 
using var Ison = 
options);

這裏對輸出進行了縮進,最後把這個options傳遞給Utf8JsonWriter的構造函數便可。 

 

再次運行: 

"title": "Programming", 
"language" • " 
"firstName": "Nick", 
"lastName" : "Carter"

如今好看多了。 

 

JsonSerializer 

前面幾節的內容可能稍微有點底層,咱們大部分時候可能只須要對C#的類進行串行化或者將JSON數據反串行化成C#類,在.NET Core 3.0裏面,咱們可使用JsonSerializer這個類來作這些事情。 

 

例子:

仍是使用以前用到的json數據: 

"post Title": "Programming" 
"language": 
"author": 
"firstName": "Nick", 
"lastName": "Carter" 
"publishedAt 
"wordCount " : 
13435, 
"isoriginal"• 
true, 
"tags" • 
"JSON API", ".NET Core"

 

而後咱們須要建創建兩個類,對應這個文件: 

public class 
BlogPost 
O references 
public 
O references 
public 
O references 
public 
O references 
public 
O references 
public 
O references 
public 
public 
I reference 
{ get; set; } 
string 
Post Title 
{ get; set; } 
string 
Language 
{ get; set; } 
Author Author 
{ get; set; 
DateTime PublishedAt 
{ get; set; } 
int 
WordCount 
{ get; set; } 
bool 
Isoriginal 
:iag$ 
{ get; set; } 
stringC] 
public class Author 
O references 
{ get; set; } 
public string 
FirstName 
O references 
{ get; set; } 
public string 
LastName

 

反串行化 

可使用JsonSerializer類的Deserialize()方法對json數據反串行化。這個方法支持三種類型的輸入參數,分別是: 

  • JSON數據的字符串 

  • Utf8JsonReader 

  • ReadOnlySpan<byte>,它裏面包含JSON數據 

 

爲了簡單一點,我直接把json文件讀取成字符串,而後傳給Deserialize方法: 

var text = 
var post = 
File. ReadAllText( path: 
"sample. json"); 
JsonSerializer . text); 
Console . WriteLine(post. PostTitle); 
Console . WriteLine($" {post .Author . FirstName} 
{post .Author . LastName}"); 
Console . WriteLine( post. PublishedAt);

而後我試圖打印出反串行化以後的一些屬性數據。可是這不會成功。由於JSON文件裏面數據的大小寫命名規範使用的是camel casing(簡單理解爲首字母是小寫的),而默認狀況下Deserializer會尋找Pascal casing這種規範(簡單理解爲每一個單詞的首字母都是大寫的)的屬性名。 

 

格式化 

爲解決這個問題,就須要使用JsonSerializerOptions類: 

var 
var 
options = 
new JsonSeria1izerOptions 
PropertyNamingPolicy = 
JsonNamingPolicy . CamelCase 
post = 
JsonSerializer.Deserialize<BlogPost>(text, options);

創建該類的一個實例,設置PropertyNamingPolicyCamelCase,而後把這個實例傳遞給Deserialize方法的第二個參數。 

 

運行看結果: 

Programmi ng 
Nick - Carter 
18/22/2819 PM

此次就沒有問題了。 

 

串行化 

JsonSerializer也支持串行化,也就是把C#數據轉化爲JSON數據: 

這裏使用了相同的options 

 

運行結果: 

 

若是想讓輸出結果更好看一些,能夠在JsonSerializerOptions裏面進行相應的設置: 

 

此次輸出結果爲: 

 

總結 

總結一下.NET Core 3.0新的JSON API 

  • Utf8JsonReader - 讀操做,快速,低級 

  • Utf8JsonWriter - 寫操做,快速,低級 

  • JsonDocument - 基於DOM,快速 

  • JsonSeriliazer - 串行化/反串行化,快速 

 

另外 JSON.NET 仍然被支持。

相關文章
相關標籤/搜索