Fastjson致命缺陷

本文深入探讨了Gson库的使用,包括基本的序列化和反序列化,以及进阶功能如泛型对象转换、自定义序列化和反序列化。在Fastjson曝出安全问题的背景下,作者分享了为何转向Gson并提供了详细的示例代码,旨在帮助开发者更好地理解和应用Gson。
摘要由CSDN通过智能技术生成

前言

这个周末被几个技术博主的同一篇公众号文章 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 字符串转化成对象的过程称为反序列化。img

Gson 基本使用

使用 Gson 框架进行序列化与反序列操作,都离不开 com.google.gson.Gson 对象,它也是 Gson 框架的关键对象,提供的公共 API 具备了多种序列化和反序列方式。

img

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;
        }
    }
}

运行该测试用例,在控制台可以看到如下日志输出:

img

从结果可以看出,默认的 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 字符串,在原测试类中补充下方测试类,并运行查看效果如下

@Test
void test_jsonObject_serialization() {
  Gson gson = new Gson();
  JsonObject jsonObject = new JsonObject();
  jsonObject.addProperty("code", 400);
  jsonObject.addProperty("message", "参数错误");
  String toJson = gson.toJson(jsonObject);
  String exceptedJson = "{\"code\":400,\"message\":\"参数错误\"}";
  Assertions.assertEquals(exceptedJson, toJson); //true
}

JsonObject 使用 addProperty(property,value) 方法只能用来添加 String,Number,Boolean,Character这四类数据, 因为内部是调用 com.google.gson.JsonObject#add, 并将 value 封装成了 JsonPrimitive 对象,然后保存到了内部自定义的 LinkedTreeMap 集合变量 members 中;如果需要在 JsonObject 对象上添加其他对象时,就需要直接使用 add(String property, JsonElement value) 方法添加一个 JsonElement 对象。这里的 JsonElement 是一个抽象类,JsonObject 和 JsonPrimitive 都继承了JsonElement,所以我们最终通过新的 JsonObject 对象来作为原 JsonObject 上的属性对象:

Gson gson = new Gson();
JsonObject jsonObject = new JsonObject();
//...
JsonObject nestJsonObject = new JsonObject();
nestJsonObject.addProperty("username", "one");
nestJsonObject.addProperty("score", 99);
jsonObject.add("data", nestJsonObject);
String toJson2 = gson.toJson(jsonObject);
System.out.println(toJson2);
// {"code":400,"message":"参数错误","data":{"username":"one","scor
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值