我很高兴,.NETCore终于来到了3.1LTS版本,并且将支持3年,我们也准备让部分业务迁移到3.1上面,不过很快我们就遇到了新的问题,就是对于Json序列化的选择;我本着清真的原则,既然选择迁移到3.1,一切都应该用官方标准或者建议方案。所以我们信心满满的选择了System.Text.Json。本文将会全面介绍System.Text.Json 和 Newtonsoft.Json 的相同和异同之处,方便需要的同学做迁移使用,对未来,我们保持期待。
文档比较 几个重要的对象在 System.Text.Json 中,有几个重量级的对象,所有的JSON互操作,都是围绕这几个对象进行,只要理解了他们各自的用途用法,就基本上掌握了JSON和实体对象的互操作。
JsonDocument提供用于检查 JSON 值的结构内容,而不自动实例化数据值的机制。JsonDocument 有一个属性 RootElement,提供对JSON文档根元素的访问,RootElement是一个JsonElement对象。
JsonElement提供对JSON值的访问,在System.Text.Json 中,大到一个对象、数组,小到一个属性、值,都可以通过 JsonElement 进行互操作
JsonPropertyJSON中最小的单元,提供对属性、值的访问
JsonSerializer提供JSON互操作的静态类,提供了一系列 Serializer/Deserialize 的互操作的方法,其中还有一些异步/流式操作方法。
JsonSerializerOptions与上面的 JsonSerializer 配合使用,提供自定义的个性化互操作选项,包括命名、枚举转换、字符转义、注释规则、自定义转换器等等操作选项。
Utf8JsonWriter/Utf8JsonReader这两个对象是整个 System.Text.Json 的核心对象,所有的JSON互操作几乎都是通过这两个对象进行,他们提供的高性能的底层读写操作。
初始化一个简单的 JSON 对象在 System.Text.Json 中,并未提供像 JToken 那样非常便捷的创建对象的操作,想要创建一个 JSON 对象,其过程是比较麻烦的,请看下面的代码,进行对比
// Newtonsoft.Json.Linq; JToken root = new JObject(); root["Name"] = "Ron"; root["Money"] = 4.5; root["Age"] = 30; string jsonText = root.ToString(); // System.Text.Json string json = string.Empty; using (MemoryStream ms = new MemoryStream()) { using (Utf8JsonWriter writer = new Utf8JsonWriter(ms)) { writer.WriteStartObject(); writer.WriteString("Name", "Ron"); writer.WriteNumber("Money", 4.5); writer.WriteNumber("Age", 30); writer.WriteEndObject(); writer.Flush(); } json = Encoding.UTF8.GetString(ms.ToArray()); }System.Text.Json 的操作便利性在这方面目前处于一个比较弱的状态,不过,从这里也可以看出,可能官方并不希望我们去直接操作 JSON 源,而是通过操作实体对象以达到操作 JSON 的目的,也可能对互操作是性能比较自信的表现吧。
封装和加载在对JSON文档进行包装的用法
var json = "{\"name\":\"Ron\",\"money\":4.5}"; var jDoc = System.Text.Json.JsonDocument.Parse(json); var jToken = Newtonsoft.Json.Linq.JToken.Parse(json);我发现MS这帮人很喜欢使用 Document 这个词,包括XmlDocument/XDocument等等。
查找元素(对象) var json = "{\"name\":\"Ron\",\"money\":4.5}"; var jDoc = System.Text.Json.JsonDocument.Parse(json); var obj = jDoc.RootElement[0];// 这里会报错,索引仅支持 Array 类型的JSON文档 var jToken = Newtonsoft.Json.Linq.JToken.Parse(json); var name = jToken["name"];你看,到查找元素环节就体现出差异了,JsonDocuemnt 索引仅支持 Array 类型的JSON文档,而 JToken 则支持 object 类型的索引(充满想象),用户体验高下立判。
那我们不禁要提问了,如何在 JsonDocument 中查找元素?答案如下。
从上面的代码来看,JsonElement 存在两个迭代器,分别是EnumerateArray和EnumerateObject;通过迭代器,你可以实现查找元素的需求。你看,MS关上了一扇门,然后又为了打开了一扇窗,还是很人性化的了。在System.Text.Json中,一切对象都是Element,Object/Array/Property,都是Element,这个概念和XML一致,但是和Newtonsoft.Json不同,这是需要注意的地方。
你也可以选择不迭代,直接获取对象的属性,比如使用下面的方法
var json = "{\"name\":\"Ron\",\"money\":4.5}"; var jDoc = System.Text.Json.JsonDocument.Parse(json); var age = jDoc.RootElement.GetProperty("age");上面这段代码将抛出异常,因为属性 age 不存在,通常情况下,我们会立即想用一个 ContainsKey 来作一个判断,但是很可惜,JsonElement 并未提供该方法,而是提供了一个 TryGetProperty 方法;所以,除非你明确知道 json 对象中的属性,否则一般情况下,建议使用 TryGetProperty 进行取值。