.NET 中的 Json 使用体验

简介: 本文主要总结介绍.NET 中的对 Json 数据使用在使用过程中的关于编码、循环引用、时间格式化的一些问题

背景

第一次接触 .Net 是2012年刚进入大学时,之后也一直作为桌面编程语言来使用。工作后,刚开始项目上更多的是使用 PHP 来快速开发,直到去年某次突然发现 .NET 竟不知道什么时候开始不仅跨平台还开源了。

看着微软的官方文档异常激动,按捺不住,拿一个项目上手试了一下,那个时候啥也不懂,嫌弃 JwtBearer 给我引了一大堆 dll,然后自己看文档用中间件造了轮子;而且那个时候我也知道有 EF core,也不懂什么 DBfirst,直接刀耕火种,手写 SQL,把自己累的够呛。当然在各种项目的使用中也或多或少出现了各种问题,现将使用 Json 格式相关的内容总结下来以供大家参考。

问题概览

中文 Unicode 和 字符转义 问题

中文 Unicode 这个问题在 ASP.NET Core 的返回中正常并不会出现,而是在控制台中使用 JsonSerializer.Serialize 将对象转为 json 时发生,解决方案也很简单,只需要通过 JsonSerializerOptions 设置要在转义字符串时使用的编码器即可。

var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };

在不需要转移特殊符号的场景下,使用 JsonSerializer.Serialize 时,传入上方的 options 即可。

当你前往官网仔细研究这个 UnicodeRanges 的时候,你会发现 Unicode 块竟然还有 YijingHexagramSymbols 易经八卦符号 ䷀(乾)䷁(坤)等共六十四卦,说实话我还是第一次发现,遥想当年汉字输入都是问题。八卦是我们东方人得天独厚的文化,“易有太极,是生两仪,两仪生四象,四象生八卦。” 八卦亦可用二进制表示,然后通过 8x8 的矩阵就成了易经六十四卦。

字符转义问题在 ASP.NET Core 的返回中正常并不会出现,而是在控制台中使用时,这个和上一个问题类似。若要最大程度地减少转义(肯定是已含上面的了),可以使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping

var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
这里需要注意的是,使用该配置后,此时你需要额外注意 XSS 或信息泄露攻击的可能。

另外,在非业务场景下,只是为了展示测试时,我们可以设置 JsonSerializerOptionsWriteIndentedtrue,这样 JSON 的返回只是整理好格式的易读形式。

以上问题更详细的说明,可以查看微软官方的文档 如何使用 System.Text.Json 自定义字符编码

时间问题

时间格式化的问题,主要是国情问题和能否直接显示给客户的问题。若是有国际化的问题,那就要额外再加一些其他逻辑处理了。其实我觉得这个丢给前端也挺好嘛,嘿嘿嘿。

现在我们在这样一个接口:

app.MapGet("/test", () =>
{
    return new
    {
        now = DateTime.Now,
        time = TimeOnly.FromDateTime(DateTime.Now),
        day = DateOnly.FromDateTime(DateTime.Now)
    };
});

需要注意的是,当前 .NET 6 是不支持 TimeOnly 和 DateTime 直接返回的,需要 .ToString(),直接返回是会报下面的错误的:

System.NotSupportedException: Serialization and deserialization of 'System.TimeOnly' instances are not supported. 

转为字符后输出结果如下:

{
  "now": "2022-10-30T14:43:02.0027311+08:00",
  "time": "14:43",
  "day": "2022/10/30"
}

在 .NET 7 的当前预览版中,已经支持了 TimeOnly 和 DateOnly 的直接序列化 https://github.com/dotnet/runtime/pull/69160

更改为 .NET7 后,其输出结果如下:

{
  "now": "2022-10-30T14:53:22.095974+08:00",
  "time": "14:53:22.0959758",
  "day": "2022-10-30"
}

可以看到返回的差异还是挺大的,如果返回不合你的心意,怎么能让其统一呢?这里就需要用到注册的用户定义的转换器,下面提供三个时间处理的样例,大家可以按需求复制粘贴,其实代码都类似的。

处理日期时间的:

using System.Text.Json;
using System.Text.Json.Serialization;
internal class JsonDateTimeConverter : JsonConverter<DateTime>
{
    public string Format { get; set; } = "yyyy-MM-dd HH:mm:ss";
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateTime.Parse(reader.GetString());
    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString(this.Format));
}

处理日期的:

using System.Text.Json;
using System.Text.Json.Serialization;
internal class JsonDateOnlyConverter : JsonConverter<DateOnly>
{
    public string Format { get; set; } = "yyyy-MM-dd";
    public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateOnly.Parse(reader.GetString());
    public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString(this.Format));
}

处理时间的:

using System.Text.Json;
using System.Text.Json.Serialization;
internal class JsonTimeOnlyConverter : JsonConverter<TimeOnly>
{
    public string Format { get; set; } = "HH:mm:ss";
    public override TimeOnly Read(ref Utf8JsonReader reader,Type typeToConvert, JsonSerializerOptions options) => TimeOnly.Parse(reader.GetString());
    public override void Write(Utf8JsonWriter writer, TimeOnly value,JsonSerializerOptions options) => writer.WriteStringValue(value.ToString(this.Format));
}

使用时可以直接引用,也可以设置格式方式。

builder.Services.Configure<JsonOptions>(options => {
    options.SerializerOptions.Converters.Add(new JsonDateTimeConverter() { Format = "yyyy年MM月dd日 HH:mm:ss" });
    options.SerializerOptions.Converters.Add(new JsonDateOnlyConverter());
    options.SerializerOptions.Converters.Add(new JsonTimeOnlyConverter());
});

输出结果:

{
  "now": "2022年10月30日 15:12:54",
  "time": "15:12:54",
  "day": "2022-10-30"
}

循环引用

循环引用,主要报错信息如下:

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 than the maximum allowed depth of 64.

以下代码是为了复现而复现,当然实际情况我们遇到的话,往往是不经意间就出现了。

internal class  User{
    public string Name {get;set;}
    public Role Role {get;set;}
}

internal class  Role{
    public string RoleName {get;set;}
    public User[] Users {get;set;}
}

app.MapGet("/test", () =>
{
    var role = new Role(){RoleName = "小组长"};
    var user = new User(){Name = "王小明"};
    
    user.Role = role;
    role.Users = new User[]{user};
    
    return new {
        user = user
    };
});

方案一:

如果循环的只是为了编码查找方便,或者说此接口没必要输出的情况下,可以直接标记为忽略,不进行 json 输出。

internal class Role
{
    public string RoleName { get; set; }

    [JsonIgnore]
    public User[] Users { get; set; }
}

方案二:

ASP.NET 中,需要配置:

builder.Services.Configure<JsonOptions>(options => {
    options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});

控制台程序或命令行打印时:

var options = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };

最后

在这里还是要再感谢一下杨老师,还好在大学的时候某次见面会加了微信,在他朋友圈看到在推录制的 .NET 6教程 。不然说不定目前我还是在刀耕火种阶段,哈哈哈……

相关文章
|
JSON 数据格式
net.sf.json.JSONObject 和org.json.JSONObject 的差别
net.sf.json.JSONObject 和org.json.JSONObject 的差别
|
3月前
|
JSON 开发框架 JavaScript
【Azure Developer】使用.Net Core解析JSON的笔记
【Azure Developer】使用.Net Core解析JSON的笔记
|
5月前
|
JSON 开发框架 API
【推荐100个unity插件之20】一个强大的JSON处理库——Newtonsoft.Json(也称为Json.NET)
【推荐100个unity插件之20】一个强大的JSON处理库——Newtonsoft.Json(也称为Json.NET)
433 0
|
5月前
|
JSON 数据格式 微服务
.NET下 支持大小写不敏感的JSON Schema验证方法
有很多应用程序在验证JSON数据的时候用到了JSON Schema。 在微服务架构下,有时候各个微服务由于各种历史原因,它们所生成的数据对JSON Object属性名的大小写规则可能并不统一,它们需要消费的JSON数据的属性名可能需要大小写无关。 遗憾的是,目前的JSON Schema没有这方面的标准,标准中都是大小写敏感的。在类似上述情况下,这给使用JSON Schema进行数据验证造成了困难。
|
6月前
|
JSON 开发框架 .NET
ASP.NET Core Web API设置响应输出的Json数据格式的两种方式
ASP.NET Core Web API设置响应输出的Json数据格式的两种方式
170 0
|
存储 JSON JavaScript
.NET MVC第九章、Web Api Json序列化与反序列化
.NET MVC第九章、Web Api Json序列化与反序列化
206 0
.NET MVC第九章、Web Api Json序列化与反序列化
|
JSON 数据格式
【.NET开发福音】使用Visual Studio将JSON格式数据自动转化为对应的类
【.NET开发福音】使用Visual Studio将JSON格式数据自动转化为对应的类
568 0
【.NET开发福音】使用Visual Studio将JSON格式数据自动转化为对应的类
|
开发框架 前端开发 .NET
ASP.NET MVC 中使用Highcharts+Ajax+Json生成动态曲线图,柱状图,饼图
ASP.NET MVC 中使用Highcharts+Ajax+Json生成动态曲线图,柱状图,饼图
144 0
ASP.NET MVC 中使用Highcharts+Ajax+Json生成动态曲线图,柱状图,饼图
|
JSON 数据格式
将JSON字符串反序列化为指定的.NET对象类型
将JSON字符串反序列化为指定的.NET对象类型
169 0
|
XML JSON JavaScript
.NET6新东西---System.Text.Json Writeable DOM
.NET6新东西---System.Text.Json Writeable DOM
181 0
.NET6新东西---System.Text.Json Writeable DOM