前言
以前.NetCore是不內建JSON庫的,所以大家都用Newtonsoft
的JSON庫,而且也確實挺好用的,不過既然官方出了標準庫,那更方便更值得我們多用用,至少不用每次都nuget安裝Newtonsoft.Json
庫了。
不過日常開發使用中會有一些問題,本文記錄一下解決方法,歡迎交流~
(文章末尾包含小彩蛋)
字元編碼問題
預設的 System.Text.Json
序列化的時候會把所有的非 ASCII 的字元進行轉義,這就會導致很多時候我們的一些非 ASCII 的字元就會變成 \uxxxx
這樣的形式,很多場景下並不太友好,我們可以配置字元編碼來解決被轉義的問題。
例子:
var testObj=new {
Name = "測試",
Value = 123
};
var json = JsonSerializer.Serialize(testObj);
Console.WriteLine(json);
輸出
{"Name":"\u6D4B\u8BD5","Value":123}
在我們序列化的時候,可以指定一個 JsonSerializeOptions
,而這個 JsonSerializeOptions
中有一個 Encoder 我們可以用來配置支援的字元編碼,不支援的就會被轉義,而預設只支援 ASCII 字元。
所以解決方法如下:
var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions()
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
})
Console.WriteLine(json);
輸出結果
{"Name":"測試","Value":123}
字元轉義問題
對於一些包含 html 標籤的文字即使指定了所有字符集也會被轉義,這是出於安全考慮。如果覺得不需要轉義也可以配置,配置使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping
即可。
示例程式碼
var testObj = new {
Name = "測試",
Value = 123,
Code = "<p>test</p>"
};
var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions {
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
});
Console.WriteLine(json);
輸出
{"Name":"測試","Value":123,"Code":"\u003Cp\u003Etest\u003C/p\u003E"}
可以看到HTML程式碼被轉義了,這很明顯就不行
解決方法
var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions {
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
輸出結果
{"Name":"測試","Value":123,"Code":"<p>test</p>"}
搞定!
物件套娃遞迴問題
這個問題在我之前的一篇文章中有詳細說到:Asp-Net-Core開發筆記:介面返回json物件出現套娃遞迴問題
當時我是用Newtonsoft.Json
來解決的,不過當我把這篇文章釋出到部落格園之後,有大佬指出.NetCore
標準庫System.Text.Json
中也有解決這個問題的方法,於是我這裡也來記錄一下~
首先建立幾個實體類
internal class EntityBase {
public string Id { get; set; }
}
internal class CrawlTask : EntityBase {
/// <summary>
/// 爬蟲名稱
/// </summary>
public string Name { get; set; }
/// <summary>
/// 建立這個爬蟲的使用者
/// </summary>
public User User { get; set; }
/// <summary>
/// 使用者ID
/// </summary>
public string? UserId { get; set; }
}
internal class User : EntityBase {
/// <summary>
/// 使用者名稱
/// </summary>
public string Name { get; set; }
/// <summary>
/// 使用者建立的爬蟲
/// </summary>
public List<CrawlTask> CrawlTasks { get; set; }
}
然後用模擬資料來重現問題
//模擬資料
var crawlTask = new CrawlTask { Name = "爬蟲名稱", UserId= "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041" };
var user = new User { Name = "使用者名稱", CrawlTasks = new List<CrawlTask> { crawlTask } };
crawlTask.User = user;
// 輸出
var json2 = JsonSerializer.Serialize(crawlTask);
Console.WriteLine(json2);
輸出結果,直接報錯
Unhandled exception. System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger tha
n the maximum allowed depth of 64. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. Path: $.User.CrawlTasks.User.CrawlTasks.U
ser.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.Us
er.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.Name.
...
我們都知道了這是物件的套娃遞迴問題了
所以接下來直接上解決方法
var json2 = JsonSerializer.Serialize(crawlTask,new JsonSerializerOptions {
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true,
ReferenceHandler = ReferenceHandler.IgnoreCycles
});
Console.WriteLine(json2);
ReferenceHandler.IgnoreCycles
方式是.Net6新增加的,可以實現和Newtonsoft.Json
裡ReferenceLoopHandling.Ignore
差不多的效果。
最終輸出效果如下
{
"Name": "爬蟲名稱",
"User": {
"Name": "使用者名稱",
"CrawlTasks": [
null
],
"Id": null
},
"UserId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
"Id": null
}
可以看到導致套娃遞迴的屬性變成了null
不過這個和Newtonsoft.Json
實現的效果還是有點差異的
在我之前的文章裡,Newtonsoft.Json
實現的效果是
{
"name": "test crawl123",
"user": {
"name": "string",
"crawlTasks": null,
"id": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041"
},
"userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
"id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
}
可以看到的是,crawlTask.user.crawlTasks
這個屬性有差別,System.Text.Json
是一個陣列,然後裡面有一個null
物件,而Newtonsoft.Json
是把這個屬性直接置為null
相比之下,我更喜歡Newtonsoft.Json
的實現,因為在前端解析的時候可以很清晰的得到一個空物件,而不是裝著空物件的陣列(有點繞口……
後記
說實話,JSON處理還是Python這類動態語言比較方便
像上面那些問題,Python加個ensure_ascii
引數就行(雖然C#也不難)
比如
import json
test_obj = {
"name": "測試",
"value": 123,
"code": "<p>test</p>"
}
print(json.dumps(test_obj, ensure_ascii=False))
有時我還喜歡加個indent
引數,這樣輸出來的JSON字串更好看
json.dumps(test_obj, ensure_ascii=False, indent=2)
輸出結果
{
"Name": "測試",
"Value": 123,
"Code": "<p>test</p>"
}