Gson源码设计学习

一、概述

在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
2
3
4
5
6
7
8
9
10
11
12
13
Gson gson = new Gson();

// 解析数据(使用Class)
String json1 = "{\"key\":\"value\"}";
Data data1 = gson.fromJson(json, Data.class);

// 解析带泛型的数据(使用TypeToken)
String json2 = "[{\"key\",\"value1\"},{\"key\",\"value2\"}]";
List<Data> list = gson.fromJson(json2, new TypeToken<List<Data>>(){}.getType());

// 序列化数据
Data data3 = new Data();
String json3 = gson.toJson(data3);

二、主要接口

TypeToken:类型处理工具类

Gson解析数据时要知道Java对象的类型(包括其泛型),TypeToken工具类可以方便的处理类型。

例如给Gson传一个List<String>类型的Type对象,可以用下面的方式。TypeToken提供了protected权限的构造函数,通过继承一个匿名类即可实例化,getType返回一个ParameterizedType实例。

1
Type type = new TypeToken<List<String>>(){}.getType();

Type是Java中的接口,表示对象类型。Type有一个实现类Class(普通类型,例如ObjectArrayList)和几个子类接口:

  • 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
2
3
4
5
6
7
8
9
10
11
12
public enum JsonToken {
BEGIN_ARRAY,
END_ARRAY,
BEGIN_OBJECT,
END_OBJECT,
NAME,
STRING,
NUMBER,
BOOLEAN,
NULL,
END_DOCUMENT
}

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
2
3
public interface JsonSerializer<T> {
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}
1
2
3
4
public interface JsonDeserializer<T> {
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException;
}

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

https://github.com/google/gson/blob/master/CHANGELOG.md

接口定义

TypeAdapter是一个抽象类,除了一些共通方法,还包含了两个抽象方法:

  • write:序列化,Java对象 --> JsonToken --> JsonWriter
  • read:反序列化,JsonReader --> JsonToken --> Java对象
1
2
3
4
5
6
7
8
public abstract class TypeAdapter<T> {

public abstract void write(JsonWriter out, T value) throws IOException;

public abstract T read(JsonReader in) throws IOException;

// ...
}

TypeAdapter与Serializer对比

TypeAdapter

JsonSerializer、JsonDeserializer

引入版本

2.0

1.x

Stream API

支持

不支持,要先生成JsonElement

内存占用

较小

较大

效率

较高

较低

作用范围

序列化 和 反序列化

序列化 或 反序列化

TypeAdapters

TypeAdapters类中包含了一些基本类型的TypeAdapter实现,具体可以自行阅读源码。

TypeAdapterFactory:创建TypeAdapter

TypeAdapterFactory用于创建TypeAdapter。传入特定的type,Factory返回相应的TypeAdapter实例。如果不支持这种类型,则返回null。

1
2
3
public interface TypeAdapterFactory {
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}

Gson、GsonBuilder

通常使用Gson对象来序列化/反序列化Json数据。

Gson对象中包含了一系列的配置属性。对于常规简单情况,可以直接用new Gson()方式创建一个全部使用默认配置的Gson实例。如果需要自定义各种配置,则可使用GsonBuilder创建。

Gson对象内部是线程安全的,因此创建了一个Gson实例后,可以在多线程之间反复使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Gson工具类封装
public class GsonUtils {

private static final Gson gson;

static {
gson = new GsonBuilder()
// ...
.create();
}

public static Gson getGson() {
return gson;
}

public static <T> T fromJson(String json, Class<T> clazz) {
return gson.fromJson(json, clazz);
}

public static <T> String toJson(T obj) {
return gson.toJson(obj);
}
}

三、Gson设计学习

解析时Java对象的创建

在解析数据时,会给Gson传入Java对象的Type。Gson需要从Type创建对象实例,其中绝大多数对象都是通过ConstructorConstructor工具类创建的。

  1. 大部分类型的实例创建,直接通过反射调用默认无参构造函数。
  2. 对于List、Map、Set等接口类型,创建默认实现类ArrayList、HashMap、HashSet等的对象。
  3. 对于没有默认构造函数的其他类型,可以向Gson注册InstanceCreator自己创建。
  4. Primitive及封装类型、数组类型等,在相应的TypeAdapter中自己创建,不调用ConstructorConstructor。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class User {
private final Context context;
private long id;
private String name;
public User(Context context) {
this.context = context;
}
// ...
}

public class UserInstanceCreator implements InstanceCreator<User> {
@Override
public User createInstance(Type type) {
return new User(ContextProvider.getDefaultContext());
}
}
1
2
3
Gson gson = new GsonBuilder()
.registerTypeAdapter(User.class, new UserInstanceCreator())
.create();

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
2
3
4
TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
if (cached != null) {
return (TypeAdapter<T>) cached;
}

遍历TypeAdapterFactory创建TypeAdapter

Gson中还有一个List<TypeAdapterFactory>,按优先级保存了若干TypeAdapterFactory。

如果上一步TypeAdapter没有缓存,则需要创建。Gson依次遍历每个TypeAdapterFactory并调用create,直到有一个Factory返回了非空的TypeAdapter对象。

1
private final List<TypeAdapterFactory> factories;
1
2
3
4
5
6
7
8
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;
}
}

递归调用问题的解决

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
2
// skipPast即为跳过的Factory,遍历比skipPast优先级更低的Factory创建DelegateAdapter
public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type);

如下就是DelegateAdapter的一个应用,可以统计Gson序列化/反序列化了多少对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StatsTypeAdapterFactory implements TypeAdapterFactory {
public int numReads = 0;
public int numWrites = 0;

public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
++numWrites;
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
++numReads;
return delegate.read(in);
}
};
}
}

自定义TypeAdapter/TypeAdapterFactory

当需要自己处理特定数据类型时,可以用GsonBuilder配置生成Gson对象,如下:

1
2
3
4
5
Gson gson = new GsonBuilder()
.registerTypeAdapter(Test1.class, new Test1Adapter())
.registerTypeHierarchyAdapter(Test2.class, new Test2Deserializer())
.registerTypeAdapterFactory(new Test3AdapterFactory())
.create();

其中:

  • 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
2
3
4
5
6
{
"code": 0,
"data": {
"key": "value"
}
}
1
2
3
4
{
"code": 1,
"data": "msg"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Response {
int code;
JsonElement data;
}

String json = "...";
Response response = new Gson().fromJson(json, Response.class);
if (response != null) {
switch (response.code) {
case 0: // ...
break;
case 1: // ...
break;
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final class TreeTypeAdapter<T> extends TypeAdapter<T> {

@Override
public T read(JsonReader in) throws IOException {
if (deserializer == null) {
return delegate().read(in);
}
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
return null;
}
return deserializer.deserialize(value, typeToken.getType(), context);
}

private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null ? d : (delegate = gson.getDelegateAdapter(skipPast, typeToken));
}

// ...
}

JsonAdapter注解:设置自定义TypeAdapter

给数据类型自定义TypeAdapter/TypeAdapterFactory或JsonSerializer/JsonDeserializer,除了用GsonBuilder配置,也可以直接用JsonAdapter注解。这个注解可作用于类和成员变量

1
2
3
4
5
6
@JsonAdapter(UserDeserializer.class)
class User {
@JsonAdapter(IdTypeAdapter.class)
public long id;
public String name;
}

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
    7
    public class MyClass {
    @SerializedName("name")
    String a;
    @SerializedName(value="name1", alternate={"name2", "name3"})
    String b;
    String c;
    }

Expose注解:字段过滤

ReflectiveTypeAdapterFactory.Adapter默认处理Java对象的每个Field(static和transient成员除外),也可以用Expose注解配置。

  • Expose注解有serialize、deserialize两个字段,默认均为true,设置为false则不处理这个字段
  • 没有Expose注解的字段默认均处理,如果设置了GsonBuilder.excludeFieldsWithoutExposeAnnotation,则跳过所有没添加Expose注解的字段。
1
2
3
4
5
6
7
8
9
public class User {
@Expose
private String firstName;
@Expose(serialize = false)
private String lastName;
@Expose (serialize = false, deserialize = false)
private String emailAddress;
private String password;
}

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