前言
这个周末被几个技术博主的同一篇公众号文章 fastjson又被发现漏洞,这次危害可导致服务瘫痪! 刷屏,离之前漏洞事件没多久,fastjson 又出现严重 Bug。目前项目中不少使用了 fastjson 做对象与JSON数据的转换,又需要更新版本重新部署,可以说是费时费力。与此同时,也带给我新的思考,面对大量功能强大的开源库,我们不能盲目地引入到项目之中,众多开源框架中某个不稳定因素就足以让一个项目遭受灭顶之灾。趁着周末,在家学习下同样具备JSON与对象转换功能的优秀开源框架 Gson,并且打算将今后项目使用 fastjson 的地方逐渐换成使用 Gson,记录下学习总结的内容,希望对小伙伴也有所帮助。
本文所涉及所有代码片段均在下面仓库中,感兴趣的小伙伴欢迎参考学习:
https://github.com/wrcj12138aaa/gson-actions
版本支持:
JDK 8
Gson 2.8.5
JUnit 5.5.1
Lomok 1.18.8
Gson 简介
在正式介绍 Gson 之前,我们可以先从官方的wiki看下 Gson 的描述,了解它是什么?
Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object。
从描述可以看出,Gson 是用于将 Java 对象与 JSON格式字符串数据相互转换的 Java 库。它起初在Google 内部广泛使用在 Android 平台 和 Java 服务端上。2008 年开源之后,成为了谷歌又一个被广泛使用的开源框架,截止目前(2019.09.08) 在GitHub 上已有1W6 多星,相同作用的类库还有 Spring Framework 中集成的 Jackson,以及阿里开源的 fastjson等。
在特性方面,Gson 提供简易的API fromJson/toJson 来实现 Java 与 JSON 之间的转换,并且能生成紧凑,可读的 JSON 字符串输出,还支持复杂对象转换和丰富的自定义表示,足以满足在日常开发中我们绝大部分的 JSON 数据处理需求。
我们通常将对象与JSON字符串间的转换称之为序列化和反序列化(Serialization/Deserialization)。将 对象转化成 JSON字符串的过程称为序列化,将JSON 字符串转化成对象的过程称为反序列化。
Gson 基本使用
使用 Gson 框架进行序列化与反序列操作,都离不开 com.google.gson.Gson 对象,它也是 Gson 框架的关键对象,提供的公共 API 具备了多种序列化和反序列方式。
Gson 对象的创建主要有两种方式:
使用 new 关键字直接创建:Gson gson = new Gson()
由 GsonBuilder 对象构建:Gson gson = new GsonBuilder().create()
通常情况下,上面两种方式创建的 Gson 对象在进行序列化与反序列操作时行为都是一样的,但是第二种方式构建 Gson 对象时,允许进行额外的行为定制,比如格式化 JSON 字符串的输出内容,是否序列化 null 值等等。
Java 序列化
简单对象的序列化
我们可以通过下面的例子来看下通过上述两种方式序列化 Java 对象的不同效果:
public class ResultTest {
@Test
void test_serialization() {
Gson gson = new Gson();
Result result = new Result(200, "成功", null);
String json = gson.toJson(result);
System.out.println("json is " + json);
Gson buildedGson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
String buildedJson = buildedGson.toJson(result);
System.out.println("buildedJson is " + buildedJson);
}
class Result {
private int code;
private String message;
private Object data;
public Result(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
运行该测试用例,在控制台可以看到如下日志输出:
从结果可以看出,默认的 Gson 对象行为序列化对象时会将 null 值的字段忽略,而 com.google.gson.GsonBuilder#serializeNulls 方法将允许 Gson 对象序列化 null 字段;并且正常序列化后的 JSON 字符串是紧凑格式,节省字符串内存,使用 com.google.gson.GsonBuilder#setPrettyPrinting 方法之后最终输出的 JSON 字符串是更易读的格式。当然除了这两个方法,GsonBuilder 还提供了许多定制序列化和反序列化行为的API,我们将后面的内容进一步讲解。
JosnObject 生成 JSON
除了上述将自定义类的对象转换成 JSON 的方式之外,还可以使用 Gson 框架提供的 JsonObject 构建普通对象,然后使用 toJson 方法生成 JSON 字符串,在原测试类中补充下方测试类,并运行查看效果如下
$ kubectl get pods --all-namespaces -o go-template --template='{
{range .items}}{
{printf "|%-20s|%-50s|%-30s|\n" .metadata.namespace .metadata.name .metadata.uid}}{www.zheshengyule.com{end}}'
|default |details-v1-64b86cd49-85vks |2e7a2a66-533e-11e8-b722-005056a1bc83|
|default |productpage-v1-84f77f8747-7tkwb |2eb4e840-533e-11e8-b722-005056a1bc83|
|default |ratings-v1-5f46655b57-qlrxp |2e89f981-533e-11e8-b722-005056a1bc83|
...
下面举两个 go-template 高级用法的例子:
range 嵌套
# 列出所有容器使用的镜像名
$ kubectl get pods - www.zheshengyule.com-all-namespaces -o go-template --template='{