System.Text.Json 中的字符编码
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
.net 5.0 中的 JsonConsole
總結
以上是生活随笔為你收集整理的System.Text.Json 中的字符编码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 陈睿学长在CUIT建校70周年校庆上的演
- 下一篇: Web实时通信,SignalR真香,不用