一、概述
在Java、Android相关的开发中,经常会用到Json自动解析框架,其中比较常见的一个就是Google推出的Gson。
关于Gson的使用,这个系列文章已经讲的很全面了,就不重复写了。
你真的会用Gson吗?Gson使用指南
http://www.jianshu.com/p/e740196225a4
Gson源码分析的文章相对较少。本文尝试对Gson源码进行简明且较全面的分析,重点关注其中的设计思想。基于Gson 2.8.0,建议结合源码阅读。
Google / Gson - GitHub
https://github.com/google/gson
文章开始前,先简单回顾一下Gson基本用法。
1 | Gson gson = new Gson(); |
二、主要接口
TypeToken:类型处理工具类
Gson解析数据时要知道Java对象的类型(包括其泛型),TypeToken工具类可以方便的处理类型。
例如给Gson传一个List<String>
类型的Type对象,可以用下面的方式。TypeToken提供了protected权限的构造函数,通过继承一个匿名类即可实例化,getType返回一个ParameterizedType实例。
1 | Type type = new TypeToken<List<String>>(){}.getType(); |
Type是Java中的接口,表示对象类型。Type有一个实现类
Class
(普通类型,例如Object
、ArrayList
)和几个子类接口:
- GenericArrayType(数组类型,例如
String[]
)- ParameterizedType(泛型类型,例如
List<String>
)- WildcardType(形如
? extends ClassA
、?super ClassB
)- TypeVariable(类型变量)
参考:Java Type详解
http://blog.csdn.net/gdutxiaoxu/article/details/68926515
JsonToken:Json流式操作API
Gson封装了一组偏底层的JsonToken API。JsonToken是一个枚举类型,每种类型表示一个原始Json元素,例如:
- BEGIN_ARRAY:对应
[
- BEGIN_OBJECT:对应
{
- NULL:对应
null
1 | public enum JsonToken { |
JsonReader、JsonWriter:JsonString转JsonToken
JsonReader和JsonWriter用于读写Json字符串,在Json字符串和JsonToken之间转换。
JsonToken、JsonReader、JsonReader通过数据流的形式操作Json,都放在包com.google.gson.stream
中,也称为Stream API。
JsonElement:Json元素树
Gson还封装了一组JsonElement API。抽象类JsonElement表示一个Json元素,每个JsonElement可包含0到多个子JsonElement,形成树结构。JsonElement的子类有:
- JsonPrimitive:Json基本类型,例如
1
,"text"
,true
- JsonObject:Json对象,例如
{"key", "val"}
- JsonArray:Json数组,例如
[{"key", "val1"}, {"key", "val2"}]
- JsonNull:Json Null元素,即
null
JsonSerializer、JsonDeserializer:JsonElement转JavaObject
JsonSerializer和JsonDeserializer用于实现JsonElement树到Java对象之间的转换。
1 | public interface JsonSerializer<T> { |
1 | public interface JsonDeserializer<T> { |
TypeAdapter:JsonToken转JavaObject
相关背景
早期的Gson例如1.6版本中,Json数据先通过JsonReader/JsonWriter转换到JsonToken流,再通过Streams工具类转换到JsonElement树,最后由JsonSerializer/JsonDeserializer转换到Java对象。
Gson在2.0版本中引入了TypeAdapter,并在2.1版本开放接口。TypeAdapter可以直接转换JsonToken流和Java对象,可以不经过JsonElement,性能得到了提高。
Version 2.0
Previous versions first parsed complete document into a DOM-style model (JsonObject or JsonArray) and then bound data against that. Gson 2 does data binding directly from the stream parser.
Version 2.1
Support for user-defined streaming type adapters
接口定义
TypeAdapter是一个抽象类,除了一些共通方法,还包含了两个抽象方法:
- write:序列化,Java对象 --> JsonToken --> JsonWriter
- read:反序列化,JsonReader --> JsonToken --> Java对象
1 | public abstract class TypeAdapter<T> { |
TypeAdapter与Serializer对比
TypeAdapter
JsonSerializer、JsonDeserializer
引入版本
2.0
1.x
Stream API
支持
不支持,要先生成JsonElement
内存占用
较小
较大
效率
较高
较低
作用范围
序列化 和 反序列化
序列化 或 反序列化
TypeAdapters
TypeAdapters类中包含了一些基本类型的TypeAdapter实现,具体可以自行阅读源码。
TypeAdapterFactory:创建TypeAdapter
TypeAdapterFactory用于创建TypeAdapter。传入特定的type,Factory返回相应的TypeAdapter实例。如果不支持这种类型,则返回null。
1 | public interface TypeAdapterFactory { |
Gson、GsonBuilder
通常使用Gson对象来序列化/反序列化Json数据。
Gson对象中包含了一系列的配置属性。对于常规简单情况,可以直接用new Gson()
方式创建一个全部使用默认配置的Gson实例。如果需要自定义各种配置,则可使用GsonBuilder创建。
Gson对象内部是线程安全的,因此创建了一个Gson实例后,可以在多线程之间反复使用。
1 | // Gson工具类封装 |
三、Gson设计学习
解析时Java对象的创建
在解析数据时,会给Gson传入Java对象的Type。Gson需要从Type创建对象实例,其中绝大多数对象都是通过ConstructorConstructor工具类创建的。
- 大部分类型的实例创建,直接通过反射调用默认无参构造函数。
- 对于List、Map、Set等接口类型,创建默认实现类ArrayList、HashMap、HashSet等的对象。
- 对于没有默认构造函数的其他类型,可以向Gson注册InstanceCreator自己创建。
- Primitive及封装类型、数组类型等,在相应的TypeAdapter中自己创建,不调用ConstructorConstructor。
1 | public class User { |
1 | Gson gson = new GsonBuilder() |
TypeAdapter获取过程
Gson在序列化/反序列化前,都需要先知道Java对象的Type,获取到对应的TypeAdapter,再进行操作。
Gson.getAdapter()方法可以根据传入的TypeToken,返回对应的TypeAdapter。下面对其代码进行分析。
TypeAdapter缓存
Gson有个ConcurrentHashMap缓存,先从缓存取已经生成了的TypeAdapter。
1 | private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache = new ConcurrentHashMap<TypeToken<?>, TypeAdapter<?>>(); |
1 | TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type); |
遍历TypeAdapterFactory创建TypeAdapter
Gson中还有一个List<TypeAdapterFactory>
,按优先级保存了若干TypeAdapterFactory。
如果上一步TypeAdapter没有缓存,则需要创建。Gson依次遍历每个TypeAdapterFactory并调用create,直到有一个Factory返回了非空的TypeAdapter对象。
1 | private final List<TypeAdapterFactory> factories; |
1 | for (TypeAdapterFactory factory : factories) { |
递归调用问题的解决
Factory在创建Adapter时,可能会嵌套递归调用Gson.getAdapter
方法导致死循环、堆栈溢出。为了解决这个问题,getAdapter在调用Factory前会先保存一个空的FutureTypeAdapter到ThreadLocal中;之后Factory再嵌套调用getAdapter时取到的是FutureTypeAdapter;当获取到最终的Adapter后再设置到这个FutureTypeAdapter中。
This thread local guards against reentrant calls to getAdapter(). In certain object graphs, creating an adapter for a type may recursively require an adapter for the same type! Without intervention, the recursive lookup would stack overflow. We cheat by returning a proxy type adapter. The proxy is wired up once the initial adapter has been created.
DelegateAdapter设计:拦截器效果
Gson中有些TypeAdapter会有类似网络框架拦截器的效果(Interceptor),例如只在部分情况处理序列化/反序列化,或插入一些操作,剩下的再交给其他匹配的TypeAdapter处理。
为了实现这种效果,Gson中设计了DelegateAdapter机制。通过调用Gson.getDelegateAdapter方法并传入跳过的Factory,Gson会遍历优先级更低的Factory创建DelegateAdapter。
1 | // skipPast即为跳过的Factory,遍历比skipPast优先级更低的Factory创建DelegateAdapter |
如下就是DelegateAdapter的一个应用,可以统计Gson序列化/反序列化了多少对象。
1 | class StatsTypeAdapterFactory implements TypeAdapterFactory { |
自定义TypeAdapter/TypeAdapterFactory
当需要自己处理特定数据类型时,可以用GsonBuilder配置生成Gson对象,如下:
1 | Gson gson = new GsonBuilder() |
其中:
- registerTypeAdapter,可传入JsonSerializer、JsonDeserializer、InstanceCreator、TypeAdapter,处理特定类型数据
- registerTypeHierarchyAdapter,可传入JsonSerializer、JsonDeserializer、TypeAdapter,处理特定类型及其子类数据
- registerTypeAdapterFactory,注册自定义的Factory
Builder中注册的JsonSerializer/JsonDeserializer/TypeAdapter都会被转换成TypeAdapterFactory,最终加入到Gson的Factory列表中。
自定义的Factory,比Gson内置的大部分Factory优先级高(除了个别特殊的内置Factory)。
TypeAdapters.JSON_ELEMENT:JsonToken转JsonElement
JSON_ELEMENT的作用是转换JsonToken和JsonElement。
有时需要自己序列化/反序列化Java对象,除了自定义TypeAdapter/Serializer,还可以直接让Gson将Json字符串转换到JsonElement树,之后再处理。
例如某个网络请求,不同的code对应不同的data结构,可以直接把data定义为JsonElement类型,由Gson解析成JsonElement树,之后再根据code在业务代码中处理。
1 | { |
1 | { |
1 | class Response { |
TreeTypeAdapter
前面提到,在Gson 1.x中只有JsonSerializer/JsonDeserializer,2.0才开始引入TypeAdapter。TreeTypeAdapter就是适配Serializer而设计的。
一方面TreeTypeAdapter将JsonToken转换到JsonElement树,再调用Serializer,这也是其名称中“Tree”的由来;另一方面TreeTypeAdapter支持只设置JsonSerializer或JsonDeserializer之一,另一个则通过调用DelegateAdapter来处理。
TreeTypeAdapter.read方法如下(write方法类似),如果有Deserializer则转换到JsonElement并使用Deserializer,否则调用DelegateAdapter处理。
1 | public final class TreeTypeAdapter<T> extends TypeAdapter<T> { |
JsonAdapter注解:设置自定义TypeAdapter
给数据类型自定义TypeAdapter/TypeAdapterFactory或JsonSerializer/JsonDeserializer,除了用GsonBuilder配置,也可以直接用JsonAdapter注解。这个注解可作用于类和成员变量。
1 |
|
JsonAdapter的实现在JsonAdapterAnnotationTypeAdapterFactory中,这个Factory的create方法会判断如果Type有JsonAdapter注解,则根据注解创建对应的Adapter/Serializer处理。
需要注意的是,由于JsonAdapterAnnotationTypeAdapterFactory在Gson中的优先级很低(具体可以看Gson构造函数源码),因此JsonAdapter注解修饰绝大多数Gson原生支持数据类型时是无效的,包括Primitive和封装类型(int/Integer/long/Long…)、String、数组、Collection及Map的子类等。
ReflectiveTypeAdapterFactory:反射解析Java对象
ReflectiveTypeAdapterFactory是Gson中优先级最低、但使用很频繁的Factory,前面所有Factory都没有处理的Type,最终都会由这个Factory创建Adapter来处理。
ReflectiveTypeAdapterFactory.Adapter通过反射读取Java对象的每个Field,然后根据相应的名字和类型,再调用其他匹配的Adapter来处理。
FieldNamingStrategy与SerializedName注解:字段名映射配置
ReflectiveTypeAdapterFactory.Adapter读取Java对象的Field,默认对应到Json中的字段名就是FieldName,但也可以改变这个规则。
-
通过GsonBuilder.setFieldNamingStrategy,可以设置FieldNamingStrategy,按照一定的规则转换命名风格。例如设置为
FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES
,则Java字段someFieldName
对应Json中的some_field_name
字段。 -
通过SerializedName注解,可以指定Field对应的Json字段名,还可以用alternate指定解析时尝试多个候选项。
1
2
3
4
5
6
7public class MyClass {
String a;
String b;
String c;
}
Expose注解:字段过滤
ReflectiveTypeAdapterFactory.Adapter默认处理Java对象的每个Field(static和transient成员除外),也可以用Expose注解配置。
- Expose注解有serialize、deserialize两个字段,默认均为true,设置为false则不处理这个字段
- 没有Expose注解的字段默认均处理,如果设置了GsonBuilder.excludeFieldsWithoutExposeAnnotation,则跳过所有没添加Expose注解的字段。
1 | public class User { |
Excluder、ExclusionStrategy、Since/Until注解:Class和字段过滤
Excluder用于忽略指定的类和Field。
前面已经提到可以使用Expose注解忽略特定字段。此外还可以忽略带有特定修饰符的Field(Modifier)、根据Since/Until注解指定的版本忽略、忽略内部类、使用ExclusionStrategy忽略等。具体用法可参考下文,不再详细介绍。
你真的会用Gson吗?Gson使用指南(三) http://www.jianshu.com/p/0e40a52c0063
实现方面,对Field的忽略,是由ReflectiveTypeAdapterFactory调用Excluder实现的。对于Class的忽略,Excluder自身也是一个TypeAdapterFactory,且优先级高于绝大多数Factory,在Excluder.create方法中判断如果Type的序列化或反序列化需要忽略,则进行相应的拦截处理。
Gson Design Document
在Gson官方的Design Document中,还列举了一些Gson设计过程中遇到的问题,例如解析时如何创建对象实例,为什么Gson中的大部分类都是final的,等等。
https://github.com/google/gson/blob/master/GsonDesignDocument.md
四、参考资料与扩展阅读
通过自定义TypeAdapter对Gson进行封装,解决一些开发中经常遇到的问题,可阅读这篇文章
Gson TypeAdapter使用技巧几例:数据免判空、解析后校验、预处理
http://www.paincker.com/gson-technic
其他参考资料
Google / Gson - GitHub
https://github.com/google/gson
你真的会用Gson吗?Gson使用指南
http://www.jianshu.com/p/e740196225a4
Java Type详解
http://blog.csdn.net/gdutxiaoxu/article/details/68926515