首页 > 其他分享 >使用.Net6中的System.Text.Json遇到几个常见问题及解决方案

使用.Net6中的System.Text.Json遇到几个常见问题及解决方案

时间:2023-10-21 11:14:18浏览次数:40  
标签:常见问题 Name Text json System Json CrawlTasks User var

前言

以前.NetCore是不内置JSON库的,所以大家都用Newtonsoft的JSON库,而且也确实挺好用的,不过既然官方出了标准库,那更方便更值得我们多用用,至少不用每次都nuget安装Newtonsoft.Json库了。

不过日常开发使用中会有一些问题,本文记录一下解决方法,欢迎交流~

字符编码问题

默认的 System.Text.Json 序列化的时候会把所有的非 ASCII 的字符进行转义,这就会导致很多时候我们的一些非 ASCII 的字符就会变成 \uxxxx 这样的形式,很多场景下并不太友好,我们可以配置字符编码来解决被转义的问题。

例子:

  1.   var testObj=new {
  2.     Name = "测试",
  3.     Value = 123
  4.   };
  5.    
  6.   var json = JsonSerializer.Serialize(testObj);
  7.   Console.WriteLine(json);

输出

{"Name":"\u6D4B\u8BD5","Value":123}

在我们序列化的时候,可以指定一个 JsonSerializeOptions,而这个 JsonSerializeOptions 中有一个 Encoder 我们可以用来配置支持的字符编码,不支持的就会被转义,而默认只支持 ASCII 字符。

所以解决方法如下:

  1.   var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions()
  2.   {
  3.       Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
  4.   })
  5.   Console.WriteLine(json);

输出结果

{"Name":"测试","Value":123}

字符转义问题

对于一些包含 html 标签的文本即使指定了所有字符集也会被转义,这是出于安全考虑。如果觉得不需要转义也可以配置,配置使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping 即可。

示例代码

  1.   var testObj = new {
  2.       Name = "测试",
  3.       Value = 123,
  4.       Code = "<p>test</p>"
  5.   };
  6.    
  7.   var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions {
  8.       Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
  9.   });
  10.   Console.WriteLine(json);

输出

{"Name":"测试","Value":123,"Code":"\u003Cp\u003Etest\u003C/p\u003E"}

可以看到HTML代码被转义了,这很明显就不行

解决方法

  1.   var json = JsonSerializer.Serialize(testObj, new JsonSerializerOptions {
  2.       Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
  3.   });

输出结果

{"Name":"测试","Value":123,"Code":"<p>test</p>"}

搞定!

对象套娃递归问题

这个问题在我之前的一篇文章中有详细说到:Asp-Net-Core开发笔记:接口返回json对象出现套娃递归问题

当时我是用Newtonsoft.Json来解决的,不过当我把这篇文章发布到博客园之后,有大佬指出.NetCore标准库System.Text.Json中也有解决这个问题的方法,于是我这里也来记录一下~

首先建立几个实体类

  1.   internal class EntityBase {
  2.       public string Id { get; set; }
  3.   }
  4.   internal class CrawlTask : EntityBase {
  5.       /// <summary>
  6.       /// 爬虫名称
  7.       /// </summary>
  8.       public string Name { get; set; }
  9.    
  10.       /// <summary>
  11.       /// 创建这个爬虫的用户
  12.       /// </summary>
  13.       public User User { get; set; }
  14.    
  15.       /// <summary>
  16.       /// 用户ID
  17.       /// </summary>
  18.       public string? UserId { get; set; }
  19.   }
  20.   internal class User : EntityBase {
  21.       /// <summary>
  22.       /// 用户名
  23.       /// </summary>
  24.       public string Name { get; set; }
  25.    
  26.       /// <summary>
  27.       /// 用户创建的爬虫
  28.       /// </summary>
  29.       public List<CrawlTask> CrawlTasks { get; set; }
  30.   }

然后用模拟数据来重现问题

  1.   //模拟数据
  2.   var crawlTask = new CrawlTask { Name = "爬虫名称", UserId= "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041" };
  3.   var user = new User { Name = "用户名", CrawlTasks = new List<CrawlTask> { crawlTask } };
  4.   crawlTask.User = user;
  5.    
  6.   // 输出
  7.   var json2 = JsonSerializer.Serialize(crawlTask);
  8.   Console.WriteLine(json2);

输出结果,直接报错

  1.   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
  2.   n the maximum allowed depth of 64. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. Path: $.User.CrawlTasks.User.CrawlTasks.U
  3.   ser.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.Us
  4.   er.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.Name.
  5.   ...

我们都知道了这是对象的套娃递归问题了

所以接下来直接上解决方法

  1.   var json2 = JsonSerializer.Serialize(crawlTask,new JsonSerializerOptions {
  2.       Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
  3.       WriteIndented = true,
  4.       ReferenceHandler = ReferenceHandler.IgnoreCycles
  5.   });
  6.   Console.WriteLine(json2);

ReferenceHandler.IgnoreCycles方式是.Net6新增加的,可以实现和Newtonsoft.JsonReferenceLoopHandling.Ignore差不多的效果。

最终输出效果如下

  1.   {
  2.     "Name": "爬虫名称",
  3.     "User": {
  4.       "Name": "用户名",
  5.       "CrawlTasks": [
  6.         null
  7.       ],
  8.       "Id": null
  9.     },
  10.     "UserId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
  11.     "Id": null
  12.   }

可以看到导致套娃递归的属性变成了null

不过这个和Newtonsoft.Json实现的效果还是有点差异的

在我之前的文章里,Newtonsoft.Json实现的效果是

  1.   {
  2.       "name": "test crawl123",
  3.       "user": {
  4.           "name": "string",
  5.           "crawlTasks": null,
  6.           "id": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041"
  7.       },
  8.       "userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
  9.       "id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
  10.   }

可以看到的是,crawlTask.user.crawlTasks这个属性有差别,System.Text.Json是一个数组,然后里面有一个null对象,而Newtonsoft.Json是把这个属性直接置为null

相比之下,我更喜欢Newtonsoft.Json的实现,因为在前端解析的时候可以很清晰的得到一个空对象,而不是装着空对象的数组(有点绕口……

后记

说实话,JSON处理还是Python这类动态语言比较方便

像上面那些问题,Python加个ensure_ascii参数就行(虽然C#也不难)

比如

  1.   import json
  2.    
  3.   test_obj = {
  4.       "name": "测试",
  5.       "value": 123,
  6.       "code": "<p>test</p>"
  7.   }
  8.   print(json.dumps(test_obj, ensure_ascii=False))

有时我还喜欢加个indent参数,这样输出来的JSON字符串更好看

json.dumps(test_obj, ensure_ascii=False, indent=2)

输出结果

  1.   {
  2.     "Name": "测试",
  3.     "Value": 123,
  4.     "Code": "<p>test</p>"
  5.  

标签:常见问题,Name,Text,json,System,Json,CrawlTasks,User,var
From: https://www.cnblogs.com/chinasoft/p/17778627.html

相关文章

  • thinkPHP5.0返回的接口返回 json数据,用了json_encode不生效,却返回的却是text/html格
    如何让返回的数据完全是json1、用SoapUI来测试借口,Content-Type不是json,而是text/html;2、自己的接口,最后的数据用了json_encode,也是不管用的;3、用header来设置Content-Type也没有效果;4、而改框架的配置default_return_type为json,这也是不可取的,整站是网站需要返回的还是te......
  • 前端常见问题总结(正在更新中)
    开发中我所遇到的前端问题1、按钮问题2、追加元素的问题3、AJAX发送的数据是数组或者对象时,会报错4、浮点数精度问题(7*0.8会等于5.6000000000000005)5、区分JS中的undefined,null,"",0和false5、误用ajax的complete1、按钮问题(1)、没有准确定位到按钮的选择器(这就不便多说,都知......
  • 08System类
    System类System系统类,主要用于获取系统属性数据和其他操作,构造方法是私有的。方法名说明staticvoidarraycopy(...)复制数组staticlongcurrentTimeMillis();获取当前系统时间,返回值是毫秒值staticvoidgc();建议JVM赶快启动垃圾回收器回收垃圾static......
  • Pixie: A System for Recommending 3+ Billion Items to 200+ Million Users in Real-
    目录概符号说明PixieEksombatchaiC.,JindalP.,LiuJ.Z.,LiuY.,SharmaR.,SugnetC.,UlrichM.andLeskovecJ.Pixie:Asystemforrecommending3+billionitemsto200+millionusersinreal-time.WWW,2018.概Pinterest的一个大规模处理图的方案(偏推理......
  • Systemd集成Golang二进制程序
    首先新建Service,名称叫做server-apivim/lib/systemd/system/server-api.service[Unit]Description=serverapi[Service]Type=simpleRestart=alwaysRestartSec=5sExecStart=/root/go/mainWorkingDirectory=/root/go/[Install]WantedBy=multi-user.targetExecStart......
  • WPF TextBox按回车键执行
    如果界面上只有一个要执行的命令时,可以直接把某个Button的IsDefault设置为True就可以。如果界面上有多个不同的执行命令的话,可以用下面的InputBindings,不同的输入框绑定不同的Command即可。<TextBoxText="{BindingProgressName,UpdateSourceTrigger=PropertyChanged}"S......
  • wpf TextBox复制文字卡顿 和迅雷的冲突
    迅雷会在后台一直监控读取用户剪切板,如果发现是下载格式,将会弹出下载框在wpf的TextBox中,快速按CtrlC会导致获取剪切板和设置剪切板的命令争抢引发System.Runtime.InteropServices.COMException这个异常通过查看源代码,发现在复制文字时,他们都不同情况的重试,暂停这可能就导致了......
  • windows命令行启动jar常见问题及解决方案
    windows端口被占用1查询端口netstat-ano2查询指定端口netstat-ano|findstr"端口号"3根据进程PID查询进程名称tasklist|findstr“进程PID号”4根据PID杀死任务taskkill-f-pid“进程PID号”5根据进程名称杀死任务taskkill-f-t-im“进程名称” ......
  • 关于 systemctl 的简单使用
    启动服务:systemctlstartvsftpd.service关闭服务:systemctlstopvsftpd.service重启服务:systemctlrestartvsftpd.service显示服务的状态:systemctlstatusvsftpd.service在开机时启用服务:systemctlenablevsftpd.service在开机时禁用服务:systemctldisablevsftpd.servic......
  • Signals and systems(1)
    LEC1IntroductionSignalsContinuoussignalsEX1.Soundsignals\(y=x(t)\)ContinuousTimesignal()Onedimensionsignal(onlyhaveonevariabletime)EX2.Images\(b=f(x,y)\)TwodimensionalsignalContinuoussignal(non-Timevariable)Dis......