您的浏览器不支持CSS3,建议使用Firfox、Chrome等浏览器,以取得最佳显示效果

Gson源码设计学习

Android 45℃ 0 2周前 (12-01)

一、概述

在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基本用法。

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实例。

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
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对象之间的转换。

public interface JsonSerializer<T> {
    public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}
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对象
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对比

TypeAdapterJsonSerializer、JsonDeserializer
引入版本2.0
Stream API支持
内存占用较小
效率较高
作用范围序列化 和 反序列化

TypeAdapters

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

TypeAdapterFactory:创建TypeAdapter

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

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

Gson、GsonBuilder

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

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

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

// 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。
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());
    }
}
Gson gson = new GsonBuilder()
    .registerTypeAdapter(User.class, new UserInstanceCreator())
    .create();

TypeAdapter获取过程

Gson在序列化/反序列化前,都需要先知道Java对象的Type,获取到对应的TypeAdapter,再进行操作。

Gson.getAdapter()方法可以根据传入的TypeToken,返回对应的TypeAdapter。下面对其代码进行分析。

TypeAdapter缓存

Gson有个ConcurrentHashMap缓存,先从缓存取已经生成了的TypeAdapter。

private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache = new ConcurrentHashMap<TypeToken<?>, TypeAdapter<?>>();
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对象。

private final List<TypeAdapterFactory> factories;
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。

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

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

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对象,如下:

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在业务代码中处理。

{
    "code": 0,
    "data": {
        "key": "value"
    }
}
{
    "code": 1,
    "data": "msg"
}
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处理。

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注解。这个注解可作用于类和成员变量

@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指定解析时尝试多个候选项。

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注解的字段。
    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

    来自为知笔记(Wiz)

    如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

    0

    暂无评论

    评论前:需填写以下信息,或 登录

    用户登录

    忘记密码?