如何更好的使用 Gson
最近工作比较忙,很久没更新了,先向大家道个歉。
今天想分享一些工作中遇到的关于 Gson 的坑,这么说其实不太准确,因为不能算是 Gson 的坑,更多的是因为旧代码产生了一些不规范的数据导致使用 Gson 时遇到了一些问题。
Gson 简介
可能有的同学不了解 Gson ,所以在分享坑之前先来介绍一下 Gson ,已经熟练使用 Gson 的同学可以直接跳到下一部分了。Gson 是Google开源的一个Java序列化库,它具有以下特点:
使用简单,只需要掌握
toJson()
和fromJson()
两个方法就可以实现 Java 对象和 JSON 字符串之间的序列化和反序列化允许将现有的不可修改的对象与 JSON 互相转换
对 Java 的泛型支持的很好
允许自定义一些对象的表现形式
支持复杂对象的序列化
使用 Gson
那现在我们就来体验一下 Gson 的第一个特性,使用简单。由团队中成员的能力参差不齐,所以一个简单易用性对这种基础组件是非常重要的。
在使用 Gson 之前,我们需要添加依赖,我们的项目中使用的是 Maven 管理依赖,所以会在 pom.xml 文件中插入以下代码:
如果你的项目使用的是 Gradle 管理依赖,你需要新增下面的代码
依赖添加好以后,就可以直接开始使用了,这里我先来定义一个简单的 POJO 类(原谅我直接使用@Data)。
来看一下输出结果
完美!是不是非常简单?
那现在我们已经学会 Gson 的基础用法了,接下来就进入正题,分享几个我在使用过程中遇到的实际问题以及解决方案。
案例分享
null 转为空字符串
在我们的使用过程中,遇到过这样的情况对于一个对象,在做序列化的时候,如果遇到了某个 item 为 null ,那么 Gson 序列化出来的结果中就不会包含这个属性,这看起来很合理,不过对于我们的项目而言,前端同学需要根据有没有这个 item 来展示不同的信息,如果有这个 item ,但是值为空,那么前端就展示「不能告诉你」,如果没有这个 item ,前端同学就会展示为「没有这个 item」。这么说可能令人有些费解,我们还是通过上面的例子来看。
如果我把 name 设置为 null ,那么序列化的结果就是这样。此时前端就会展示为「用户没有姓名信息」,如果我把 name 设置成空字符串,那么序列化结果就会不同。
这时我们的前端同学就会告诉用户,此用户不想展示名称。
我们现在想要避免出现第一种情况,虽然说可以约定不能把name设置为null,但是这种约定就很容易导致 bug 的产生,尤其是对于刚刚加入团队的新同学来说,他们可能会在不经意间就做了这样一个操作,在code review 的时候也可能会出现遗漏。因此我选择定义一种TypeAdapter来约束我们序列化的工作。
这里可以先介绍一下 Gson 中 TypeAdapter 的使用方法,TypeAdapter 可以帮助我们自定义序列化/反序列化方式,它的使用也比较简单,首先我们需要定义一个自己的 Adapter 类,让它继承 TypeAdapter 类
然后自己重写 read 和 write 方法,这里我们需要的是 write 方法。
其中参数 value 就是传入的对象属性,我们判断它是 null,就将其转化为空字符串。
写好 Adapter 类之后,我们在新建 Gson 的时候需要注册我们刚刚定义的 Adapter。
GsonBuilder 提供了 registerTypeAdapter 这个方法,可以直接为 String 类型都注册上我们自己的Adapter。
这时再将 name 设置为 null,序列化结果就是我们期望的结果了。
数字和 Boolean 到底用哪个
我们在开发过程中还遇到了这样一个问题,在和另一个 node 写的服务做交互时,我们发现,node 服务返回给我们的 JSON 对应的 Boolean 类型字段的值是0或1。
还用我们前面的例子来讲,如果 node 服务返回给我们的数据是这样的 JSON 字符串
那么我们在反序列化时就会报错
错误信息写的很清楚,我们的 isVip 字段是一个 Boolean 类型的,但是 JSON 中却是数字类型,Gson 没办法识别了。
这时我们可以让 node 服务来修改,也可以选择自己做适配。
自己做适配的话,有两种方式,一种是把 isVip 字段改成 Number 类型,但是由于 isVip 只可能存在两种值(是/否),用 Number 类型不是很合适。另一种方式就是再写一个 Adapter 来做适配,这次我们就需要重写 read 方法了。
针对我们的问题,只需要处理 NUMBER 类型就可以了,不过这里我还兼容了 STRING 类型,把字符串的 true/false 转换成 Boolean 类型。你可以根据业务需要自行适配。例如有些团队可能会将 null 值认为是 false,这里直接修改一下就好。
写好了 Adapter 以后还是别忘了注册。
扩展一点
细心的同学一定注意到了 JsonToken 这个类了,这是 Gson 中对于 JSON 符号类型的定义。它包含以下几种
BEGIN_ARRAY
END_ARRAY
BEGIN_OBJECT
END_OBJECT
NAME
STRING
NUMBER
BOOLEAN
NULL
END_DOCUMENT
从名称上就可以分辨出来BEGIN_ARRAY
和END_ARRAY
是对数组的标记,BEGIN_OBJECT
和END_OBJECT
是对对象的标记,NAME
标记的是 JSON 中的「key」,STRING
、NUMBER
、BOOLEAN
和NULL
都是 JSON中值的类型,END_DOCUMENT
是 JSON 流结束的标识。
讨论
最后留一个问题大家可以和我一起讨论,我们在做反序列化时还遇到了BT的字符串的 null,它本身所属的字段是 Map 类型,这样的 Adapter 应该怎么写呢?
版权声明: 本文为 InfoQ 作者【Jackey】的原创文章。
原文链接:【http://xie.infoq.cn/article/608453c2a9b31709b36dfc4ad】。文章转载请联系作者。
评论