Java 常用的 JSON 序列化与反序列化工具实践
JSON 简介:
JSON(Java Script Object Notation)是一种轻量级的数据交换格式,通常用于在不同系统之间传输数据。它基于 JavaScript 对象语法,但已成为一种独立于语言的格式。JSON 数据以键值对的形式组织,易于阅读和编写。
为什么要使用 JSON?
1.简单易用:JSON 的语法简单,易于理解和编写,可以快速地进行数据交换。
2.跨平台支持:JSON 可以被多种编程语言解析和生成,可以在不同的平台和语言之间进行数据交换和传输。
3.数据交换格式:JSON 是一种标准的数据交换格式,可以在 Web 应用程序中广泛使用,如前后端数据交互、API 接口数据传输等。
4.轻量级:JSON 的数据格式轻量级,传输数据时占用带宽较小,可以提高数据传输速度。
5.易于扩展:JSON 的数据结构灵活,支持嵌套对象和数组等复杂的数据结构,便于扩展和使用。
6.安全性:JSON 数据格式是一种纯文本格式,不包含可执行代码,不会执行恶意代码,因此具有较高的安全性。
什么时候会使用 JSON?
1.前后端数据传输:当 Web 应用程序需要进行前后端数据传输时,可以使用 JSON 格式来传输数据,以便前后端之间进行数据交互。
2.API 接口数据传输:当使用 API 接口进行数据传输时,可以使用 JSON 格式来传输数据,以便多个系统之间进行数据交互。
3.存储数据:当需要存储数据时,可以使用 JSON 格式来存储数据,以便后续的读取、修改和删除等操作。
4.日志记录:当需要记录日志时,可以使用 JSON 格式来记录日志信息,以便后续的分析和查询。
5.配置文件:当需要存储配置文件时,可以使用 JSON 格式来存储配置信息,以便后续的读取和修改操作。
JSON 序列化与反序列化实践。
java 中比较常用的 JSON 工具 fastjson,fastjson2,jackson,gson。实践的内容是新增字段的场景,各个工具的兼容性以及不同工具间的兼容性。
前置条件
各个 JSON 工具的版本号:
fastjson
fastjson2
jackson
gson
实体类:
现在三个类 Person 、OldFamily、Family 三个类,Family 是在 OldFamily 中增加了新属性 oldPerson。主要是为了模拟数据处理时,新老数据的兼容问题。
fastjson 序列化与反序列化
![](https://static001.geekbang.org/infoq/e9/e9f47d20d8ef7d8aa9bd043d0a88e85d.png)
通过程序运行结果,可以观察到在 Family 序列化结果中,persons 属性中的第三个 person 实例是 oldPerson 实例的引用,而 yongPerson 属性的值是 persons 属性中下标为 0 的实例的引用。这已经不是标准的 JSON 格式,而是 fastjson 的特性。当增加 oldPerson 属性后,Family 序列化的结果在反序列化为 OldFamily 对象实例时,persons 属性中有一个 person 实例为空。虽然反序列化不会报错,但程序将无法得到预期的结果。
为解决这个问题,fastjson 在序列化时是默认的顺序是按照属性字段的字母顺序排序。你也可以通过注解的方式指定顺序,将新增加的属性放到 orders 的最后。
![](https://static001.geekbang.org/infoq/a1/a181691537096daa9372dfa4d167fc5b.png)
通过序列化结果可以看到 oldPerson 这个新增加的属性已经引用了 persons 属性中下标为 2 的实例。反序列化之后的 OldFamily 对象 persons 属性中的三个实例都有值,不会再有 null 值。程序还可以确保正确运行。
fastjson1.X 可以通过 SerializerFeature 参数来输出标准 JSON 格式
![](https://static001.geekbang.org/infoq/3b/3b075521dc674869493c8ad8a11a603b.png)
通过程序运行结果可以看出,当指定序列化参数 SerializerFeature.DisableCircularReferenceDetect 时,是以标准的 Json 格式输出。
fasJson2 序列化与反序列化
在 fastjson2 中,将对象序列化为 JSON 格式时,默认情况下就是标准的 JSON 格式。你可以通过设置com.alibaba.fastjson2.JSONWriter.Feature
参数值为`JSONWriter.Feature.ReferenceDetection
![](https://static001.geekbang.org/infoq/87/87ca2fb1391dfd4ae747df06a20d8122.png)
即使用标准的 JSON 格式作为默认序列化方式是合理的。非标准化的方式应当需要特殊指定。在使用 fastjson2 版本 2.0.26 时,当设置com.alibaba.fastjson2.JSONWriter.Feature
参数值为JSONWriter.Feature.ReferenceDetection
时,会导致序列化和反序列化成 Family 实例结果得不到预期结果。
而在反序列化为 OldFamily 实例结果正确是因为没有 oldPerson 属性。
然而,在最新的 2.0.48 版本中,可以正常进行序列化和反序列化。以下是 fastjson2 的 2.0.48 版本的运营结果
![](https://static001.geekbang.org/infoq/fe/fefe2acc0b199b5c19fde9d071445abd.png)
gson 序列化与反序列化
在 gson 中,默认情况下,当在Fimaly
类中新增了oldPerson
属性时,Fimaly
的序列化结果可以正确地反序列化成OldFimaly
类的实例。
![](https://static001.geekbang.org/infoq/65/6509da1e01ad3d49f7d7da880db2475b.png)
Jackson 序列化与反序列化
Jackson 默认情况下,当在Fimaly
类中新增了oldPerson
属性时,Fimaly
类的序列化结果无法正确反序列化成OldFimaly
类实例。是因为 Jackson 在默认情况下进行反序列化时,要求所有属性都必须存在才能正确反序列化。
![](https://static001.geekbang.org/infoq/ed/ed4388aa3a5c226e443c0393a437ef37.png)
解决方案 1 通过代码设置 ObjectMapper 中 DeserializationFeature 的属性
![](https://static001.geekbang.org/infoq/11/1189503ba5aaef04cd0720cdf118c8bc.png)
解决方案 2 在类上使用
![](https://static001.geekbang.org/infoq/d2/d2d7056b4d0b6d6a483cd8b41fcb6be6.png)
![](https://static001.geekbang.org/infoq/d0/d0d8087b3ae029621f157fe6f3504f86.png)
在使用 jackson 进行序列化和反序列化时,最好指定不进行属性检测。否则,在类新增属性的情况下就无法实现兼容。
fastjson 默认序列化 fastjson2 反序列化
![](https://static001.geekbang.org/infoq/cf/cf50cde812dd9beb757cbe1c29713bf6.png)
通过测试结果可以看出,fastjson2 可以反序列化出 fastjson 默认序列化的 json 结果,说明了 fastjson2 兼容了 fastjson。毕竟都是阿里出品。
fastjson2 序列化 fastjson 反序列化
![](https://static001.geekbang.org/infoq/98/9812212582e19b8ae953df0e5100aeea.png)
通过测试结果可以看出,fastjson2 可以反序列化出 fastjson 默认序列化的 json 结果,说明了 fastjson2 兼容了 fastjson。毕竟都是阿里出品。
fastjson 默认序列化 jackson 反序列化
![](https://static001.geekbang.org/infoq/13/1387bb81ccbc65b7cf9f9583dad4d73d.png)
通过结果可以看出,使用 jackson 进行反序列化时,没有指定了 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 为 false,那么默认值为 true,会把“$ref”作为属性进行检测,无论是 OldPerson 还是 Person 都没有此属性。会抛出异常。
![](https://static001.geekbang.org/infoq/ea/ea4120394df7380c1eb932a6362ca3c6.png)
通过结果可以看出,使用 jackson 进行反序列化时,指定了 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 为 false 没有报异常,但是 person 对象是以属性的默认值实例化的,没有得到想要的结果。
fastjson 默认序列化 gson 反序列化
![](https://static001.geekbang.org/infoq/92/929d613ce54fd280f0d9439d84f614a9.png)
通过结果可以看出,使用 gson 进行反序列化时没有报异常,但是 person 对象是以属性的默认值实例化的,没有得到预期结果。
总结:
1.使用 fastjson 时,默认的序列化方式会对于具有相同对象的多个引用,除了第一个会以标准的 JSON 文本输出,其他引用会以“$ref”的方式输出文本。为了以标准的 JSON 格式输出文本,可以使用SerializerFeature.DisableCircularReferenceDetect
参数。而 fastjson2 的默认序列化输出是标准的 JSON 格式,若需要具有 fastjson 默认序列化特性的场景,可以指定com.alibaba.fastjson2.JSONWriter.Feature
参数值为JSONWriter.Feature.ReferenceDetection
。
2.在使用 fastjson 或者 fastjson2 的特性,具有相同对象的多个引用,除了第一个会以标准的 JSON 文本输出,其他引用会以“$ref”的方式输出文本。在新增类属性字段的情况下,一定要把新增的属性放到最后序列化,确保在反序列化成没有新增属性类实例时,得不到预期结果造成线上事故。
3.fastjson 和 fastjson2 在一定程度上是兼容的,但也存在版本差异和 Bug,因此在使用时需要进行充分的测试验证。另外,gson、jackson 和 fastjson2 的默认序列化方式也是标准 JSON 格式。对于 jackson,若要在反序列化时兼容不存在的属性的 JSON 文本成为对象实例,需要设定DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
为 false。
4.在使用 JSON 序列化工具时,需要注意每个工具的特点,尤其是在使用各自具有的特性时,要留意兼容性问题。在进行 JSON 工具版本升级时,也要进行充分的测试验证,确保有单元测试来保证质量。
5.若作为与外部系统交互的 JSON 格式数据,需要以标准化的数据格式进行存储或传输。
6.以上内容仅代表个人观点和一小部分实践,欢迎大家一起探讨。
作者:京东物流 吴宪彬
来源:京东云开发者社区
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/5400557e754d28089793cf760】。文章转载请联系作者。
评论