ASP.NET Core 3.1系列(29)——System.Text.Json实现JSON的序列化和反序列化
创始人
2024-05-12 11:05:20
0

1、前言

Web开发中,JSON数据可以说是无处不在。由于具有轻量、易读等优点,JSON已经成为当前主流的数据传输格式。在ASP.NET Core 3.0之前,大多数项目都会使用Newtonsoft.Json组件来实现JSON的序列化和反序列化操作,而从ASP.NET Core 3.1开始,微软提供的System.Text.Json已经相当出色,其效率相比前者可以说是有过之而无不及,下面就来介绍一下它的使用方法。

2、引入System.Text.Json

新建一个Web API项目,使用NuGet引入如下组件:

System.Text.Json

在这里插入图片描述
新建一个实体类Person,代码如下:

using System;namespace App
{public class Person{public int Id { get; set; }public string PersonName { get; set; }public string PersonGender { get; set; }public DateTime? BirthOfDate { get; set; }}
}

新建一个控制器PersonController,添加一个Get方法,代码如下:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = new DateTime(2000, 1, 1) },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = new DateTime(2000, 2, 2) },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = new DateTime(2000, 3, 3) }};return new JsonResult(list);}}
}

运行结果如下所示:

[{"id":1,"personName":"\u5F20\u4E09","personGender":"\u7537","birthOfDate":"2000-01-01T00:00:00"},{"id":1,"personName":"\u674E\u56DB","personGender":"\u5973","birthOfDate":"2000-02-02T00:00:00"},{"id":1,"personName":"\u738B\u4E94","personGender":"\u7537","birthOfDate":"2000-03-03T00:00:00"}]

上面的代码实现了一个简单的JSON序列化操作,但里面的问题也有很多,比如中文乱码、时间格式等等,下面就来说一说如何在System.Text.Json中去设置它们。

3、序列化操作

3.1、JSON数据编码

上面的运行结果显示中文乱码,我们可以使用JsonSerializerOptions中的Encoder来指定编码格式,代码如下:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = new DateTime(2000, 1, 1) },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = new DateTime(2000, 2, 2) },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = new DateTime(2000, 3, 3) }};return new JsonResult(list, new JsonSerializerOptions{Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping});}}
}

运行结果如下所示,可以发现中文编码正确。

[{"Id":1,"PersonName":"张三","PersonGender":"男","BirthOfDate":"2000-01-01T00:00:00"},{"Id":2,"PersonName":"李四","PersonGender":"女","BirthOfDate":"2000-02-02T00:00:00"},{"Id":3,"PersonName":"王五","PersonGender":"男","BirthOfDate":"2000-03-03T00:00:00"}]

3.2、JSON文本格式化

默认输出的JSON文本是未经格式化的,如果你希望JSON看起来清楚一些,可以设置WriteIndented属性值,代码如下:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = new DateTime(2000, 1, 1) },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = new DateTime(2000, 2, 2) },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = new DateTime(2000, 3, 3) }};return new JsonResult(list, new JsonSerializerOptions{Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,WriteIndented = true});}}
}

运行结果如下所示,可以发现JSON文本已经被格式化了。

[{"Id": 1,"PersonName": "张三","PersonGender": "男","BirthOfDate": "2000-01-01T00:00:00"},{"Id": 2,"PersonName": "李四","PersonGender": "女","BirthOfDate": "2000-02-02T00:00:00"},{"Id": 3,"PersonName": "王五","PersonGender": "男","BirthOfDate": "2000-03-03T00:00:00"}
]

3.3、JSON字段命名格式

上面的输出结果遵循的是首字母大写的帕斯卡命名格式,如果希望输出结果采用驼峰式进行命名,则可以对PropertyNamingPolicy进行设置,代码如下:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = new DateTime(2000, 1, 1) },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = new DateTime(2000, 2, 2) },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = new DateTime(2000, 3, 3) }};return new JsonResult(list, new JsonSerializerOptions{Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,WriteIndented = true,PropertyNamingPolicy = JsonNamingPolicy.CamelCase});}}
}

运行结果如下所示,可以发现JSON文本采用的是驼峰式命名。

[{"id": 1,"personName": "张三","personGender": "男","birthOfDate": "2000-01-01T00:00:00"},{"id": 2,"personName": "李四","personGender": "女","birthOfDate": "2000-02-02T00:00:00"},{"id": 3,"personName": "王五","personGender": "男","birthOfDate": "2000-03-03T00:00:00"}
]

3.4、忽略JSON中的null值

在序列化时,如果对象的属性值为null,则结果中也会显示为null。如果希望忽略null值,则可以对DefaultIgnoreCondition进行设置,代码如下:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = null },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = null },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = null }};return new JsonResult(list, new JsonSerializerOptions{Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,WriteIndented = true,PropertyNamingPolicy = JsonNamingPolicy.CamelCase,DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull});}}
}

运行结果如下所示,可以发现BirthOfDate字段的值被忽略了。

[{"id": 1,"personName": "张三","personGender": "男"},{"id": 2,"personName": "李四","personGender": "女"},{"id": 3,"personName": "王五","personGender": "男"}
]

3.5.、JSON忽略只读字段

一般来说,由于只读字段无法进行反序列化操作,因此在序列化时可以考虑忽略。现在对Person代码进行修改,添加一个只读字段Info,代码如下:

using System;namespace App
{public class Person{public int Id { get; set; }public string PersonName { get; set; }public string PersonGender { get; set; }public DateTime? BirthOfDate { get; set; }public string Info{get { return $"姓名:{PersonName},性别:{PersonGender},出生日期:{BirthOfDate}"; }}}
}

我们可以对JsonSerializerOptions中的IgnoreReadOnlyProperties字段进行设置,从而忽略只读字段,代码如下:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = new DateTime(2000, 1, 1) },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = new DateTime(2000, 2, 2) },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = new DateTime(2000, 3, 3) }};return new JsonResult(list, new JsonSerializerOptions{Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,WriteIndented = true,PropertyNamingPolicy = JsonNamingPolicy.CamelCase,DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,IgnoreReadOnlyProperties = true});}}
}

运行结果如下所示,可以发现Info字段并没有被序列化。

[{"id": 1,"personName": "张三","personGender": "男","birthOfDate": "2000-01-01T00:00:00"},{"id": 2,"personName": "李四","personGender": "女","birthOfDate": "2000-02-02T00:00:00"},{"id": 3,"personName": "王五","personGender": "男","birthOfDate": "2000-03-03T00:00:00"}
]

3.6、JSON中的时间格式

在上面的代码中,时间字段BirthOfDate的序列化结果有一些问题,如何把它序列化成我们熟悉的时间格式呢?首先定义一个类DateTimeJsonConverter,该类继承JsonConverter,代码如下:

using System;
using System.Text.Json;
using System.Text.Json.Serialization;namespace App
{public class DateTimeJsonConverter : JsonConverter{public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){if (reader.TokenType == JsonTokenType.String){if (DateTime.TryParse(reader.GetString(), out DateTime dateTime)){return dateTime;}}return reader.GetDateTime();}public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options){writer.WriteStringValue(value.ToString("yyyy年MM月dd日 HH时mm分ss秒"));}}
}

然后在JsonSerializerOptions中的Converters集合中加入它即可,代码如下:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = new DateTime(2000, 1, 1, 0, 0, 0) },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = new DateTime(2000, 2, 2) },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = new DateTime(2000, 3, 3) }};JsonSerializerOptions options = new JsonSerializerOptions{Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,WriteIndented = true,PropertyNamingPolicy = JsonNamingPolicy.CamelCase,DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull};options.Converters.Add(new DateTimeJsonConverter());return new JsonResult(list, options);}}
}

运行结果如下所示,可以发现BirthOfDate字段的值已经被格式化。

[{"id": 1,"personName": "张三","personGender": "男","birthOfDate": "2000年01月01日 00时00分00秒"},{"id": 2,"personName": "李四","personGender": "女","birthOfDate": "2000年02月02日 00时00分00秒"},{"id": 3,"personName": "王五","personGender": "男","birthOfDate": "2000年03月03日 00时00分00秒"}
]

4、反序列化操作

4.1、常规操作

JSON的反序列化操作比较简单,只需要调用Deserialize即可。代码如下:

using Microsoft.AspNetCore.Mvc;
using System.Text.Json;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public ActionResult Get(){string json = "{\"Id\":1,\"PersonName\":\"张三\",\"PersonGender\":\"男\",\"BirthOfDate\":\"2000-01-01T00:00:00\"}";Person person = JsonSerializer.Deserialize(json);return $"姓名:{person.PersonName}\n性别:{person.PersonGender}\n出生日期:{person.BirthOfDate}";}}
}

运行结果如下所示:

姓名:张三
性别:男
出生日期:2000/1/1 0:00:00

4.2、特殊情况的处理

在进行反序列化操作时,有一种情况需要特别注意,那就是属性值末尾存在逗号的情况。在JavaScript中,下面的代码是允许的,即:在BirthOfDate的属性值后面允许添加一个逗号:

{"Id": 1,"PersonName": "张三","PersonGender": "男","BirthOfDate": "2000-01-01T00:00:00",
}

但这种情况会导致反序列化报错,代码如下:

using Microsoft.AspNetCore.Mvc;
using System.Text.Json;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public ActionResult Get(){string json = "{\"Id\":1,\"PersonName\":\"张三\",\"PersonGender\":\"男\",\"BirthOfDate\":\"2000-01-01T00:00:00\",}";Person person = JsonSerializer.Deserialize(json);return $"姓名:{person.PersonName}\n性别:{person.PersonGender}\n出生日期:{person.BirthOfDate}";}}
}

运行结果如下图所示:

在这里插入图片描述
如果要允许这种末尾添加逗号的情况,需要设置JsonSerializerOptionsAllowTrailingCommas属性,代码如下:

using Microsoft.AspNetCore.Mvc;
using System.Text.Json;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public ActionResult Get(){string json = "{\"Id\":1,\"PersonName\":\"张三\",\"PersonGender\":\"男\",\"BirthOfDate\":\"2000-01-01T00:00:00\",}";Person person = JsonSerializer.Deserialize(json, new JsonSerializerOptions{AllowTrailingCommas = true});return $"姓名:{person.PersonName}\n性别:{person.PersonGender}\n出生日期:{person.BirthOfDate}";}}
}

运行结果如下所示:

姓名:张三
性别:男
出生日期:2000/1/1 0:00:00

5、全局配置JSON

上面的代码是在单个方法中设置JSON操作属性,如果当前存在很多方法,则必然会导致代码臃肿。我们可以在全局对JSON进行配置,代码如下:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;namespace App
{public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){services.AddControllers().AddJsonOptions(options =>{// 设置编码格式options.JsonSerializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;// 是否格式化文本options.JsonSerializerOptions.WriteIndented = true;// 添加时间格式化转换器options.JsonSerializerOptions.Converters.Add(new DateTimeJsonConverter());// 字段采用驼峰式命名options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// 忽略null值options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;// 忽略只读字段options.JsonSerializerOptions.IgnoreReadOnlyProperties = true;// 允许属性值末尾存在逗号options.JsonSerializerOptions.AllowTrailingCommas = true;// 处理循环引用类型options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;});}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseHttpsRedirection();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});}}
}

在全局进行配置后,Controller中的方法就不需要单独配置了,代码如下:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;namespace App.Controllers
{[Route("api/[controller]/[action]")][ApiController]public class PersonController : ControllerBase{[HttpGet]public JsonResult Get(){List list = new List{new Person { Id = 1, PersonName = "张三", PersonGender = "男", BirthOfDate = new DateTime(2000, 1, 1, 0, 0, 0) },new Person { Id = 2, PersonName = "李四", PersonGender = "女", BirthOfDate = new DateTime(2000, 2, 2) },new Person { Id = 3, PersonName = "王五", PersonGender = "男", BirthOfDate = new DateTime(2000, 3, 3) }};return new JsonResult(list);}}
}

运行结果如下所示:

[{"id": 1,"personName": "张三","personGender": "男","birthOfDate": "2000年01月01日 00时00分00秒"},{"id": 2,"personName": "李四","personGender": "女","birthOfDate": "2000年02月02日 00时00分00秒"},{"id": 3,"personName": "王五","personGender": "男","birthOfDate": "2000年03月03日 00时00分00秒"}
]

6、结语

本文简单介绍了ASP.NET Core中关于JSON序列化和反序列化操作,主要通过System.Text.Json来实现。如果你觉得微软提供的JSON序列化工具不好用,那也可以使用Newtonsoft.Json,我也会在下一篇博客中介绍关于Newtonsoft.Json的使用方法。

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...