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<Person> list = new List<Person>
{
new Person { Id = 1, PersonName = "张三", PersonGender = "男", PersonAge = 30 },
new Person { Id = 2, PersonName = "李四", PersonGender = "女", PersonAge = 31 },
new Person { Id = 3, PersonName = "王五", PersonGender = "男", PersonAge = 32 }
};
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<Person> list = new List<Person>
{
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<Person> list = new List<Person>
{
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<Person> list = new List<Person>
{
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<Person> list = new List<Person>
{
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<Person> list = new List<Person>
{
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<DateTime>
,代码如下:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace App
{
public class DateTimeJsonConverter : JsonConverter<DateTime>
{
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<Person> list = new List<Person>
{
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<string> Get()
{
string json = "{\"Id\":1,\"PersonName\":\"张三\",\"PersonGender\":\"男\",\"BirthOfDate\":\"2000-01-01T00:00:00\"}";
Person person = JsonSerializer.Deserialize<Person>(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<string> Get()
{
string json = "{\"Id\":1,\"PersonName\":\"张三\",\"PersonGender\":\"男\",\"BirthOfDate\":\"2000-01-01T00:00:00\",}";
Person person = JsonSerializer.Deserialize<Person>(json);
return $"姓名:{person.PersonName}\n性别:{person.PersonGender}\n出生日期:{person.BirthOfDate}";
}
}
}
运行结果如下图所示:
如果要允许这种末尾添加逗号的情况,需要设置JsonSerializerOptions
的AllowTrailingCommas
属性,代码如下:
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class PersonController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
string json = "{\"Id\":1,\"PersonName\":\"张三\",\"PersonGender\":\"男\",\"BirthOfDate\":\"2000-01-01T00:00:00\",}";
Person person = JsonSerializer.Deserialize<Person>(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<Person> list = new List<Person>
{
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
的使用方法。