Gson是一个相对简单的库,没有那么多功能,从设计上也并不想让别人去扩展它,它只想安安静静地做一个Json序列化库,简单而实用。
简单说
Gson提供两种方式创建Gson实例
new Gson()
:快速创建,默认配置,快速使用new GsonBuilder().setxxxxx().create()
:完整方式创建,支持一些自定义化的配置
Gson突出一个简单,API如此,功能更是如此,大致列一下其支持的功能
- 基于field的序列化与反序列化:基本特性
- 支持自定义属性名:@SerializedName
- 支持反序列化时指定泛型信息:TypeToken
- 支持排除某个字段:transient关键字排除单个字段、按照可见性修饰符排除、@Expose主动选择
- 支持自定义序列化和反序列化逻辑:JsonSerializer、JsonDeserializer,或者它们的集合体:TypeAdapter
基础能力
什么注释也不用加,啥也不用干,直接就能使用
1 | class Resource1<T> { |
要点
- 序列化用
gson.toJson(xxx)
,反序列化用gson.fromJson(jsonString, 类型信息)
- 忽略字段可以用transient关键字
- 自定义字段名用@SerializedName注解,实际上这是Gson能够个性化配置的唯四之一
- 对于泛型擦除的情况,在反序列化时通过TypeToken指定:
object : TypeToken<Resource1<JsonObject>>() {}.type
。这一点和Jackson和Fastjson的TypeReference类似
排除字段
1 | class Resource4 { |
GsonBuilder().excludeFieldsWithoutExposeAnnotation()
设置只暴露@Expose的数据- @Expose标记字段
自定义实例创建器
Gson反序列化时创建对象的逻辑
- 首先寻找是否存在目标类的无参构造函数,有则用它创建实例
- 其次寻找是否存在用户自定义的实例创建器
- 然后如果目标类是原生类型,则直接查找对应类型的构造器并创建实例
- 都没有,则用sun.misc.Unsafe创建实例
一般不推荐使用Unsafe创建实例,要么提供无参构造方法,要么提供实例创建器,这里有一个后者的例子
1 | class Resource3(var id: Int, var type: ResourceType?) { |
输出
1 | 有参构造函数被执行了 |
PS:Gson默认忽略掉内部类,因为它没有无参构造函数
自定义序列化器
老规矩,自定义LocalDateTime的序列化和反序列化逻辑,为此Gson提供了三种类型可供定义,要么定义序列化器、要么反序列化器,要么同时有。
1 | class Resource5 { |
- 可通过@JsonAdapter局部指定
- 也可通过
GsonBuilder().registerTypeAdapter
全局注册
多态
Gson原生不支持多态,但可通过一些其它方式实现,以下是官方推荐的方式(尽管很傻)
1 | fun main() { |
- 如果一个集合中有多重类型,反序列化时,先得到JsonArray,再针对具体元素应用具体类型
- 这就不咋科学,还有一种方式是RuntimeTypeAdapterFactory,这非官方推荐的方式,所以要用多态还是别用Gson了
树模型
Gson的树模型还是简单的,只有JsonArray、JsonObject以及JsonElement三个类,但是API不大友好,限制的比较死
- 添加一般属性就要调用addProperty方法,且只支持String、Boolean、Number、Character四种类型
- 添加对象或数组属性就得用add方法
- 不支持fluent API
1 | fun main() { |
支持设置啥能力
穷举一下GsonBuilder,有啥能力,可以看到,其实没啥能力。
- 设置序列化和反序列化的排除策略
- 设置各种类型适配器,用于控制类型序列化和反序列化时的行为
- 关闭内部类的序列化
- Html格式转义
- 序列化名称控制
- 输出格式化
- 版本控制(@Since和@Until注解可设置POJO的版本,有点类似@JsonView的功能,但感觉非常鸡肋)
- 设置日期格式
- 设置字段名命名策略
基本原理
加上Gson,前前后后看了五个序列化库,除了Java,原理结构上都大同小异,只是在序列化和反序列化的具体算法上有所差别,扣得比较细节,尤其是Fastjson用了很多奇技淫巧,硬是把速度提了上去。
- 对于序列化,首先得到序列化器,再用序列化器将实际对象写入流
- 对反序列化,首先得到反序列化器,再得到目标类的实例,再用反序列化器从流中读取内容塞入目标实例
至于Gson,它比较与众不同的点在于
- 序列化时,直接使用了StringWriter做写操作,而不是自己维护输出流和缓冲区
- Json格式的实际写入在com.google.gson.stream.JsonWriter,内部套StringWriter
- 写入状态控制与Jackson采用树状结构不同,它采用了栈(一个一维数组,数组的大小即当前层次结构的深度,数组的值即当前所处结构的类型)的方式维护,参见com.google.gson.stream.JsonWriter#stack
- 对未自定义序列化器的类型来说,使用com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.Adapter达成
- 对于序列化,它直接使用了反射获取符合要求的字段,然后写入writer
- 对于反序列化,它使用前文”自定义实例创建器”所说的方式创建对象,然后通过反射写入目标对象
总结
通篇看起来,Gson定位清晰,目标明确,文档和代码规范,用起来也比较轻松。只是功能简单,原理也简单,直接使用StringWriter和反射,就是一个功能性的Json库,看不大出有什么性能优化,因此可以推测,Gson的性能不会太出色。