使用.Net6中的System.Text.Json遇到几个常见问题及解决方案
原文链接:https://blog.csdn.net/zls365365/article/details/124162096
前言
以前.NetCore是不内置JSON库的,所以大家都用Newtonsoft
的JSON库,而且也确实挺好用的,不过既然官方出了标准库,那更方便更值得我们多用用,至少不用每次都nuget安装Newtonsoft.Json
库了。
字符编码问题
默认的 System.Text.Json 序列化的时候会把所有的非 ASCII 的字符进行转义,这就会导致很多时候我们的一些非 ASCII 的字符就会变成 \uxxxx 这样的形式,很多场景下并不太友好,我们可以配置字符编码来解决被转义的问题。
例子:
1 2 3 4 5 6 7 | var testObj= new { Name = "测试" , Value = 123 }; var json = JsonSerializer.Serialize(testObj); Console.WriteLine(json); |
输出
1 | { "Name" : "\u6D4B\u8BD5" , "Value" :123} |
在我们序列化的时候,可以指定一个 JsonSerializeOptions,而这个 JsonSerializeOptions 中有一个 Encoder 我们可以用来配置支持的字符编码,不支持的就会被转义,而默认只支持 ASCII 字符。
所以解决方法如下:
1 2 3 4 5 | var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }) Console.WriteLine(json); |
输出结果
1 | { "Name" : "测试" , "Value" :123} |
字符转义问题
对于一些包含 html 标签的文本即使指定了所有字符集也会被转义,这是出于安全考虑。如果觉得不需要转义也可以配置,配置使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping 即可。
示例代码
1 2 3 4 5 6 7 8 9 10 | 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); |
输出
1 | { "Name" : "测试" , "Value" :123, "Code" : "\u003Cp\u003Etest\u003C/p\u003E" } |
可以看到HTML代码被转义了,这很明显就不行
解决方法
1 2 3 | var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); |
输出结果
1 | { "Name" : "测试" , "Value" :123, "Code" : "<p>test</p>" } |
搞定!
对象套娃递归问题
这个问题在我之前的一篇文章中有详细说到:Asp-Net-Core开发笔记:接口返回json对象出现套娃递归问题
当时我是用Newtonsoft.Json来解决的,不过当我把这篇文章发布到博客园之后,有大佬指出.NetCore标准库System.Text.Json中也有解决这个问题的方法,于是我这里也来记录一下~
首先建立几个实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 ; } } |
然后用模拟数据来重现问题
1 2 3 4 5 6 7 8 | //模拟数据 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); |
输出结果,直接报错
1 2 3 4 5 | 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. ... |
我们都知道了这是对象的套娃递归问题了
所以接下来直接上解决方法
1 2 3 4 5 6 | 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差不多的效果。
最终输出效果如下
1 2 3 4 5 6 7 8 9 10 11 12 | { "Name" : "爬虫名称" , "User" : { "Name" : "用户名" , "CrawlTasks" : [ null ], "Id" : null }, "UserId" : "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041" , "Id" : null } |
可以看到导致套娃递归的属性变成了null
不过这个和Newtonsoft.Json实现的效果还是有点差异的
在我之前的文章里,Newtonsoft.Json实现的效果是
1 2 3 4 5 6 7 8 9 10 | { "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的实现,因为在前端解析的时候可以很清晰的得到一个空对象,而不是装着空对象的数组(有点绕口……
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· DeepSeek “源神”启动!「GitHub 热点速览」
· 上周热点回顾(2.17-2.23)
2022-07-01 给三十几万条数据加序号(sqlserver与excel)
2022-07-01 .csv文件与excel文件的区别
2022-07-01 人脸数据库的特征提取