就算是这样,使用 GetProperty/TryGetProperty 得到的值,还是一个 JsonElement 对象,并不是你期望的“值”。所以 JsonElement 很人性化的提供了各种 GetIntxx/GetString 方法,但是就算如此,还是可能产生意外,思考下面的代码:
var json = "{\"name\":\"Ron\",\"money\":4.5,\"age\":null}"; var jDoc = System.Text.Json.JsonDocument.Parse(json); var property = jDoc.RootElement.GetProperty("age"); var age = property.GetInt32();上面的代码,最后一行将抛出异常,因为你尝试从一个 null 到 int32 的类型转换,怎么解决这种问题呢,又回到了 JsonElement 上面来,他又提供了一个对值进行检查的方法
if (property.ValueKind == JsonValueKind.Number) { var age = property.GetInt32(); }这个时候,程序运行良好,JsonValueKind 枚举提供了一系列的类型标识,为了进一步缩小内存使用率,Json团队用心良苦的将枚举值声明为:byte 类型(够抠)
public enum JsonValueKind : byte { Undefined = 0, Object = 1, Array = 2, String = 3, Number = 4, True = 5, False = 6, Null = 7 }看到这里,你是不是有点想念 Newtonsoft.Json 了呢?别着急,下面我给大家介绍一个宝贝 System.Json.dll。
System.Json 基本介绍System.Json 提供了对JSON 对象序列化的基础支持,但是也是有限的支持,请看下图
System.Json 目前已合并到 .NETCore-3.1 中,如果你希望使用他,需要单独引用
Install-Package System.Json -Version 4.7.0这个JSON互操作包提供了几个常用的操作类型,从下面的操作类不难看出,提供的支持是非常有限的,而且效率上也不好说
System.Json.JsonArray System.Json.JsonObject System.Json.JsonPrimitive System.Json.JsonValue首先,JsonObject是实现 IDictionary 接口,并在内部维护一个 SortedDictionary<string, JsonValue> 字典,所以他具备字典类的一切操作,比如索引等等,JsonArray 就更简单,也是一样的实现 IList 接口,然后同样的在内部维护一个 List 链表,以实现数组功能,对象的序列化都是通过 JsonValue 进行操作,序列化的方式也是非常的简单,就是对对像进行迭代,唯一值得称道的地方是,采用了流式处理。
使用System.Json操作上面的查找过程如下 var obj = System.Json.JsonObject.Parse("{\"name\":\"ron\"}"); if (obj.ContainsKey("age")) { int age = obj["age"]; }令人遗憾的是,虽然 System.Json 已经合并到 .NETCore-3.1 的路线图中;但是,System.Text.Json 不提供对 System.Json 的互操作性,我们期待以后 System.Text.Json 也能提供 System.Json 的操作便利性。
序列化和反序列化基本知识已经介绍完成,下面我们进入 System.Text.Json 的内部世界一探究竟。
互操作思考下面的代码
// 序列化 var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30 }; var json = JsonSerializer.Serialize(user); // 输出 {"Name":"Ron","Money":4.5,"Age":30} // 反序列化 user = JsonSerializer.Deserialize<UserInfo>(json);目前为止,上面的代码工作良好。让我们对上面的代码稍作修改,将 JSON 字符串进行一个转小写的操作后再进行反序列化的操作
// 输出 {"name":"Ron","money":4.5,"age":30} // 反序列化 user = JsonSerializer.Deserialize<UserInfo>(json);上面的代码可以正常运行,也不会抛出异常,你可以得到一个完整的 user 对象;但是,user对象的属性值将会丢失!这是因为 System.Text.Json 默认采用的是区分大小写匹配的方式,为了解决这个问题,我们需要引入序列化操作个性化设置,请参考下面的代码,启用忽略大小写的设置
// 输出 {"name":"Ron","money":4.5,"age":30} var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }; // 反序列化 user = JsonSerializer.Deserialize<UserInfo>(json,options); 格式化JSON现在你可以选择对序列化的JSON文本进行美化,而不是输出上面的压缩后的JSON文本,为了实现美化的效果,你仅仅需要在序列化的时候加入一个 WriteIndented 设置
var options = new JsonSerializerOptions() options.WriteIndented = true; var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30, Remark = "你好,欢迎!" }; var json = JsonSerializer.Serialize(user, options); // 输出 { "Name": "Ron", "Money": 4.5, "Age": 30, "Remark": "\u4F60\u597D\uFF0C\u6B22\u8FCE\uFF01" }你看,就是这么简单,但是你也发现了,上面的 Remark 属性在序列化后,中文被转义了,这就是接下来要解决的问题
字符转义的问题在默认情况下,System.Text.Json 序列化程序对所有非 ASCII 字符进行转义;这就是中文被转义的根本原因。但是在内部,他又允许你自定义控制字符集的转义行为,这个设置就是:Encoder,比如下面的代码,对中文进行转义的例外设置,需要创建一个 TextEncoderSettings 对象,并将 UnicodeRanges.All 加入允许例外范围内,并使用 JavaScriptEncoder 根据 TextEncoderSettings创建一个 JavaScriptEncoder 对象即可。
var encoderSettings = new TextEncoderSettings(); encoderSettings.AllowRanges(UnicodeRanges.All); var options = new JsonSerializerOptions(); options.Encoder = JavaScriptEncoder.Create(encoderSettings); options.WriteIndented = true; var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30, Remark = "你好,欢迎!" }; var json = JsonSerializer.Serialize(user, options); // 输出 { "Name": "Ron", "Money": 4.5, "Age": 30, "Remark": "你好,欢迎!" }