.net5 core webapi进阶之一:System.Text.Json的用法详解(上篇)JSON的读写
从本篇开始写 .net5 core webapi 进阶系列,先从JSON这种数据格式开始,原因如下:
1 . 够简洁;
2 . 易于理解;
3 . 其格式和面向对象的语言天然匹配;
4 . 多语言(Javascript 、C# 、Java 等)支持;
毫无疑问,JSON是不同语言,不同系统之间进行数据交换的最好选择,没有之一,
如果XML代表着上一个10年,那么JSON将统治下一个10年,也许更久。。。
一 、System.Text.Json 的内部结构。
System.Text.Json.dll 程序集中有两个名称空间,分别是 System.Text.Json 和 System.Text.Json.Serialization,
用反编译工具打开System.Text.Json并只显示公有类型和成员,得到如下的类/结构:
System.Text.Json用于读写JSON,
System.Text.Json.Serialization用于JSON的序列化和反序列化。
二 、新建一个.net5 core webapi 项目演示System.Text.Json的用法。
项目名称为webapidemo2,按照惯例,
删除根目录下的 WeatherForecast.cs 文件和Controllers目录下的 WeatherForecastController.cs 文件,
新建一个API控制器(不是MVC控制器)文件 JsonDemoController.cs,如下:
在此文件中引用如下几个名称空间:
using System.Buffers;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using static System.Text.Json.JsonElement;
在项目根目录下新建一个Models的文件夹,
然后添加一个 Company.cs 和 User.cs 的类文件作为序列化和反序列化使用 ,如下:
Company.cs 和 User.cs 的代码先不写,在演示序列化和反序列化的时候再补充完成。
我们在 JsonDemoController.cs 这个控制器中编写7个终结点,分别完成如下7个功能:
1 . 终结点: JsonDemo1();作用:return 一个最简单的 JSON 字符串;
{"userid":123, "username":"jacky"}
2 . 终结点: JsonDemo2();作用:return 一个带字符串数组的 JSON 字符串;
{"userid":123, "username":"jacky", "mobile":["13244445555","18966667777"]}
3 . 终结点: JsonDemo3();作用:return 一个带对象数组的 JSON 字符串;
{"userid":123, "username":"jacky", "employees":[{"userid":834,"username":"simon"},{"userid":835,"username":"roger"}]}
4 . 终结点: JsonDemo4();作用:用 JsonDocument 类解析一个JSON文件,读取指定属性的值;
文件位于根目录下,名称叫 appcom.json,格式如下:
{ "CompanyName": "top", "Website": "www.xxxx.com", "Address": "开曼群岛", "Boss": {"userid": 1001, "username": "王五", "mobile": "13300047775"}, "Employees": [ { "userid": 2113, "username": "张三", "mobile": "13378465709" }, { "userid": 3266, "username": "李四", "mobile": "13378465709" } ], "PhoneNumber": ["010-93847485","15890276458"] }
5 . 终结点:JsonDemo5();作用:用 Utf8JsonReader 遍历 JSON 文件(文件同 4.)
6 . 终结点:JsonDemo6();作用:将 Company 和 User 实例序列化成 JSON 对象;
7 . 终结点:JsonDemo7();作用:将 JSON 对象反序列化成 Company 和 User 实例;
三 、编码,7个终结点的代码如下。
1 . 终结点: JsonDemo1();
[Route("json1")] [HttpGet] public string Demo1() { //{"userid":123, "username":"jacky"} //表示基于堆、使用字节数组存储数据的输出接收器,可向其中写入数据。 ArrayBufferWriter<byte> buffer = new ArrayBufferWriter<byte>(); //utf8编码的JSON写入器,可以将int,string,array等数据写入到JSON中 Utf8JsonWriter writer = new Utf8JsonWriter(buffer); writer.WriteStartObject(); //表示一个开头花括号 "{" //写入属性及其值,这两行语句可以换成 writer.WriteNumber("userid", 123); writer.WritePropertyName("userid"); writer.WriteNumberValue(123); writer.WriteString("username", "jacky"); writer.WriteEndObject(); //表示一个结尾花括号 "}" writer.Flush();//写入到buffer //ReadOnlySpan表示的存储区有更好的性能和内存开销 ReadOnlySpan<byte> span = buffer.WrittenSpan; string strJson = Encoding.UTF8.GetString(span); return strJson; }
打开POSTMAN ,用GET方式访问网址:http://localhost:61946/api/jsondemo/json1 得到如下结果:
2 . 终结点: JsonDemo2();
[Route("json2")] [HttpGet] public string Demo2() { //{"userid":123, "username":"jacky", "mobile":["13244445555","18966667777"]} ArrayBufferWriter<byte> buffer = new ArrayBufferWriter<byte>(); Utf8JsonWriter writer = new Utf8JsonWriter(buffer); writer.WriteStartObject(); writer.WriteNumber("userid", 123); writer.WriteString("username", "jacky"); //写入json数组开始,这两行语句可以换成 writer.WriteStartArray("mobile"); writer.WritePropertyName("mobile"); writer.WriteStartArray();//写入json数组开始,表示一个开头中括号[ writer.WriteStringValue("13244445555");//简单的值对象输出用对应的Value结尾的方法 writer.WriteStringValue("18966667777"); writer.WriteEndArray();//写入json数组结束,表示一个结束中括号] writer.WriteEndObject(); writer.Flush(); ReadOnlySpan<byte> span = buffer.WrittenSpan; string strJson = Encoding.UTF8.GetString(span); return strJson; }
打开POSTMAN ,用GET方式访问网址:http://localhost:61946/api/jsondemo/json2 得到如下结果:
3 . 终结点: JsonDemo3();
[Route("json3")] [HttpGet] public string Demo3() { //{"userid":123, "username":"jacky", "employees":[{"userid":834,"username":"simon"},{"userid":835,"username":"roger"}]} //mock一个泛型List<User>供下面使用 List<User> userList = new List<User>(); userList.Add(new User { UserId = 834, UserName = "simon" }); userList.Add(new User { UserId = 835, UserName = "roger" }); ArrayBufferWriter<byte> buffer = new ArrayBufferWriter<byte>(); Utf8JsonWriter writer = new Utf8JsonWriter(buffer); writer.WriteStartObject(); writer.WriteNumber("userid", 123); writer.WriteString("username", "jacky"); writer.WriteStartArray("employees"); foreach (User user in userList) //遍历写入User对象 { writer.WriteStartObject(); writer.WriteNumber("userid", user.UserId); writer.WriteString("username", user.UserName); writer.WriteEndObject(); } writer.WriteEndArray(); writer.WriteEndObject(); writer.Flush(); ReadOnlySpan<byte> span = buffer.WrittenSpan; string strJson = Encoding.UTF8.GetString(span); return strJson; }
打开POSTMAN ,用GET方式访问网址:http://localhost:61946/api/jsondemo/json3 得到如下结果:
4 . 终结点: JsonDemo4();
[Route("json4")] [HttpGet] public string Demo4() { StringBuilder sb = new StringBuilder(); string filePath = Path.GetFullPath("./appcom.json"); using (FileStream stream = System.IO.File.OpenRead(filePath)) { JsonDocument doc = JsonDocument.Parse(stream); JsonElement root = doc.RootElement; //得到单个属性的值 string addr = root.GetProperty("Address").GetString(); sb.Append("公司地址 : " + addr + Environment.NewLine); //属性的值是数组时取值 ArrayEnumerator arr = root.GetProperty("PhoneNumber").EnumerateArray(); sb.Append("联系电话 : "); foreach (JsonElement ele in arr) { sb.Append(ele.GetString() + ","); } sb.Append(Environment.NewLine); //属性的值是对象时取值 sb.Append("老板名字 : "); ObjectEnumerator arr2 = root.GetProperty("Boss").EnumerateObject(); while (arr2.MoveNext()) { if (arr2.Current.Name == "username") { sb.Append(arr2.Current.Value.GetString() + ","); } } sb.Append(Environment.NewLine); //属性的值是对象数组时取值 sb.Append("员工名单 : "); ArrayEnumerator arr3 = root.GetProperty("Employees").EnumerateArray(); foreach (JsonElement ele in arr3) { sb.Append(ele.GetProperty("username").GetString() + ","); } } return sb.ToString(); }
打开POSTMAN ,用GET方式访问网址:http://localhost:61946/api/jsondemo/json4 得到如下结果:
5 . 终结点: JsonDemo5();
[Route("json5")] [HttpGet] public string Demo5() { string filePath = Path.GetFullPath("./appcom.json"); ClearFileBom(filePath);//去除文件中的BOM头,避免解析错误 byte[] contents = System.IO.File.ReadAllBytes(filePath); JsonReaderOptions options = new JsonReaderOptions { AllowTrailingCommas = true, //忽略属性末尾多余的逗号 CommentHandling = JsonCommentHandling.Skip //跳过注释 }; //变量span也可用构造方法new ReadOnlySpan<byte>(contents);初始化 ReadOnlySpan<byte> span = contents.AsSpan<byte>(); Utf8JsonReader reader = new Utf8JsonReader(span, options); StringBuilder sb = new StringBuilder(); while (reader.Read()) //遍历所有属性 { sb.Append("depth:" + reader.CurrentDepth + "---"); //当前属性深度 sb.Append("JsonTokenType:" + reader.TokenType.ToString()); //属性token类型 sb.Append("------【" + Encoding.UTF8.GetString(reader.ValueSpan) + "】"); //属性名称 switch (reader.TokenType) { case JsonTokenType.String: sb.Append(" --- value is=" + reader.GetString()); //不同的属性值类型用不同的GetXxx()取值 break; case JsonTokenType.Number: sb.Append(" --- value is=" + reader.GetInt32()); break; } sb.Append(Environment.NewLine); } return sb.ToString(); } private void ClearFileBom(string filePath) { //以UTF-8编码读取带BOM头文件的内容 Encoding end = new UTF8Encoding(true); string str = string.Empty; using (StreamReader reader = new StreamReader(filePath, end)) { str = reader.ReadToEnd(); } //以UTF-8编码重新写入不带BOM头文件的内容 end = new UTF8Encoding(false); using (StreamWriter writer = new StreamWriter(filePath, false, end)) { writer.Write(str); } }
打开POSTMAN ,用GET方式访问网址:http://localhost:61946/api/jsondemo/json5 得到如下结果:
可以看到按顺序输出了整个JSON信息。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人