博觀而約取,厚積而薄發。這篇文章主要講述System.Text.Json 中的字符編碼相關的知識,希望能為你提供幫助。
參考鏈接:https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-character-encoding
System.Text.Json 中的字符編碼
Intro
默認的 System.Text.Json
序列化的時候會把所有的非 ASCII 的字符進行轉義,這就會導致很多時候我們的一些非 ASCII 的字符就會變成 \\uxxxx
這樣的形式,很多場景下並不太友好,我們可以配置字符編碼來解決被轉義的問題
Sample
首先來看一個簡單的示例:
var testObj = new { Name = "小明", Age = 10 }; WriteLine(JsonSerializer.Serialize(testObj));
輸出結果如下:
{"Name":"\\u5C0F\\u660E","Age":10}
可以看到,我們的中文沒有直接顯式出來,而是被轉義了,這可讀性一下子就大打折扣了,接着我們來嘗試讓他工作
在我們序列化的時候,可以指定一個 JsonSerializeOptions
,而這個 JsonSerializeOptions
中有一個 Encoder
我們可以用來配置支持的字符編碼,不支持的就會被轉義,而默認只支持 ASCII 字符
我們可以配置 Encoder
來支持中文,如下所示:
WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions() { Encoder = javascriptEncoder.Create(UnicodeRanges.BasicLatin, new UnicodeRange(0x4E00, 8000)) })); WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions() { Encoder = javaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) })); WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }));
我們可以通過 JavaScriptEncoder.Create
來自定義支持的字符范圍,要指定一個 Unicode 范圍
在 System.Text.Unicode.UnicodeRanges
這個靜態類中定義了一些靜態屬性來比較方便的使用各個語言對應的字符集范圍,當然如果你明確的知道字符從哪里開始,有多個個字符也可以自定義,如上面的第一種方式,但是這種不是特別推薦,因為你知道的范圍並不一定是最准確的而且可能會有變更
推薦還是直接使用 UnicodeRanges
里定義好的字符集,如第二種方式,第二種方式這里的 UnicodeRanges.CjkUnifiedIdeographs
就包含了中文字符,去網上查了一下這個 CjkUnifiedIdeographs
代表中文(Chinese)、日文(Japanese )、韓文(Korean)的字符集合
中日韓統一表意文字(英語:CJK Unified Ideographs),也稱統一漢字、統漢碼(英語:Unihan),目的是要把分別來自中文、日文、韓文、越南文、壯文、琉球文中,起源相同、本義相同、形狀一樣或稍異的表意文字,在ISO 10646及萬國碼標准賦予相同編碼。
如果你不確定是哪種字符集或者有全球化的需求,可以直接使用 UnicodeRanges.All
來支持所有的字符,如上面第三種方式
上面的示例輸出結果如下:
{"Name":"小\\u660E","Age":10} {"Name":"小明","Age":10} {"Name":"小明","Age":10}
除此之外還有一個地方可能會需要,對於一些包含 html 標簽的文本即使指定了所有字符集也會被轉義,這是出於安全考慮。如果覺得不需要轉義也可以配置,配置使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping
即可,示例如下:
var testObj = new { Name = "小明", Age = 10, Description = "<h1>這是標題</h1>" }; WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) })); WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }));
輸出結果如下:
{"Name":"小明","Age":10,"Description":"\\u003Ch1\\u003E這是標題\\u003C/h1\\u003E"} {"Name":"小明","Age":10,"Description":"<h1>這是標題</h1>"}
Console Logging
之前曾經介紹過 JsonLogging
.NET5 的一個新特性,可以參考 .net 5.0 中的 JsonConsoleJsonConsole
來格式化 Console 的日志為 Json,使用默認的配置然后會發現日志中會有很多這種 \\uxxxx
的東西,看起來怪怪的,如下圖所示:
后來測試發現默認支持的字符集也只是 ASCII 字符,而且有些字符會出於安全考慮也會轉義,微軟也是支持配置 Encoder
的,不過不是 JsonSerializeOptions
, 而是 JsonWriterOptions
,我們可以在注冊 JsonConsole
的時候配置 Encoder
,示例如下:
var builder = WebApplication.CreateBuilder(args); builder.Logging.AddJsonConsole(options => { options.JsonWriterOptions = new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; });
這里我們直接使用比較寬松的轉義規則,因為我們的場景是日志不會直接渲染在頁面上,所以個人覺得不必要求的太嚴格
加了上面的配置之后的日志如下:
這樣的日志看起來就舒服多了,可讀性也會更好一些
More
在指定 Unicode 字符集范圍的時候需要把 UnicodeRanges.BasicLatin
也加上,其他的字符串沒有包含基本的 ASCII 字符,否則基本的 ASCII 字符也會被轉義
你也可以通過 TextEncoderSettings
來配置只支持的字符,示例如下:
var encoderSettings = new TextEncoderSettings(); encoderSettings.AllowCharacters('\\u0436', '\\u0430'); encoderSettings.AllowRange(UnicodeRanges.BasicLatin); var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(encoderSettings) }; var jsonString = JsonSerializer.Serialize(weatherForecast, options);
針對 JavaScriptEncoder.UnsafeRelaxedJsonEscaping
的使用需要注意安全問題
-
它不轉義 HTML 敏感字符,如
<
、>
、&
和'
。 -
它不提供任何針對 XSS 或信息泄露攻擊(如客戶端和服務器在字符集方面不一致所可能導致的攻擊)的額外深度防御保護。
不要將原始 UnsafeRelaxedJsonEscaping
序列化的結果用到 HTML 頁面或 <script>
元素
References
-
https://github.com/WeihanLi/SparkTodo/blob/master/SparkTodo.API/Program.cs#L22
-
https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-character-encoding?WT.mc_id=DT-MVP-5004222
-
https://github.com/WeihanLi/SamplesInPractice/blob/master/JsonSample/SystemTextJsonSample/EncoderSample.cs
-
https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-character-encoding?WT.mc_id=DT-MVP-5004222
-
https://en.wikipedia.org/wiki/CJK_Unified_Ideographs