博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一个容错的Gson新世界
阅读量:5771 次
发布时间:2019-06-18

本文共 18305 字,大约阅读时间需要 61 分钟。

1. 闯入背景:

公司项目中使用Gson框架对服务器传过来的Json数据进行解析,而服务器后台数据很大程度上是通过运营后台人员配置。由于各种原因运营可能将某一字段类型配置错误,比如集合类型配置成字符串类型。虽然业务层会进行异常的捕获,但是仅因为一个字段的错误,导致整个Json数据失效,因小失大,甚至可能会造成重大损失,比如直播间礼物墙,因为一个礼物的某一个字段的错误,导致整个礼物墙展示为空,在线上环境这个算是重大事故了。于是,一个对基本类型容错的Gson改造库的需求油然而生,对于错误的数据以默认值填充。

干货地址:

2. Gson官方库地址:

3. 前提说明

a. 当前分析的Gson版本号为2.8.1。

b. Gson的处理过程主要分为两个流向,一个是序列化,将javabean对象转化为json字符串;另一个是反序列化,将json字符串映射成javabean对象。
c. 这两个流向处理前都有一个共同的操作,从传入的java实例对象或者字节码对象中获取 TypeAdapter,对于序列化就通过Jsonwriter进行写,对于反序列化就通过JsonReader进行读,所以此篇只分析Gson读的过程,写处理操作流程一样。

4. Gson 关键列的梳理

  • Gson 开发者直接使用的类,只对输入和输出负责。
  • TypeToken 封装“操作类”(Gson.fromJson(json,People.class、Gson.toJson(new People)) 两处的People都是操作类)的类型。
  • TypeAdapter 直接操作序列化与反序列化的过程,所以该抽象类中存在read()和write方法。
  • TypeAdapterFactory 用于生产TypeAdapter的工厂类。
  • GsonReader和GsonWriter是Gson处理内容的包装流,核心的操作有:
    • peek() 流中下一个需要处理的内容
    • nextName() 读取json的key
    • nextString() 读取一个String类型的value
    • nextInt() 读取一个String类型的value
    • nextBoolean() 读取一个Boolean类型的value
    • ...

5. 源码分析。

从Gson.from(json, People.class) 突入

fromJson(json,Peolple.class)的调用链    public 
T fromJson(String json, Class
classOfT) throws JsonSyntaxException { Object object = fromJson(json, (Type) classOfT); return Primitives.wrap(classOfT).cast(object); } public
T fromJson(String json, Type typeOfT) throws JsonSyntaxException { if (json == null) { return null; } StringReader reader = new StringReader(json); T target = (T) fromJson(reader, typeOfT); return target; } public
T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { JsonReader jsonReader = newJsonReader(json); T object = (T) fromJson(jsonReader, typeOfT); assertFullConsumption(object, jsonReader); return object; } public
T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { boolean isEmpty = true; boolean oldLenient = reader.isLenient(); reader.setLenient(true); try { reader.peek(); isEmpty = false; TypeToken
typeToken = (TypeToken
) TypeToken.get(typeOfT); TypeAdapter
typeAdapter = getAdapter(typeToken); T object = typeAdapter.read(reader); return object; } ... }复制代码

上面是从fromJson(String json, Class classOfT)切入,亦或者是从fromJson(JsonElement json, Class classOfT)也好,最终都是由 fromJson(JsonReader reader, Type typeOfT)处理。

整个Json的解析过程分三步过程:

  • TypeToken对象的获取
  • 根据TypeToken获取TypeAdapter对象
  • 由TypeAdapter对象解析json字符串

根据以上的三步,我们逐一突破


我们先从简单的入手,请记住我们的例子:

gson.fromJson("hello gson",String.class)

1. TypeToken的获取

public static TypeToken
get(Type type) { return new TypeToken(type); }复制代码

没什么好瞅的~ 看new吧!

TypeToken(Type type) {    this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));    this.rawType = (Class
) $Gson$Types.getRawType(this.type); this.hashCode = this.type.hashCode(); }复制代码

采用契约式对传入的type判空处理,然后获取type的(type、rawType和hashcode),分别看看type和rawtype的获取流程

1. type的获取(type的华丽包装)
public static Type canonicalize(Type type) {    if (type instanceof Class) {      Class
c = (Class
) type; return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c; } else if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType) type; return new ParameterizedTypeImpl(p.getOwnerType(), p.getRawType(), p.getActualTypeArguments()); } else if (type instanceof GenericArrayType) { GenericArrayType g = (GenericArrayType) type; return new GenericArrayTypeImpl(g.getGenericComponentType()); } else if (type instanceof WildcardType) { WildcardType w = (WildcardType) type; return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds()); } else { // type is either serializable as-is or unsupported return type; }复制代码

进入条件的筛选,第一个if还是好理解,后面的是什么鬼? 不用着急,待我给施主梳理,之前Gson.from(json, People.class)的调用链中有一个fromJson(Reader json, Type typeOfT) ,用户使用时的切入点如果是它就可能是筛选情况的其他条件,此返回的type相对于对传入的java类型进行的类型的重新包装。

2. rawType的获取(type的简单粗暴说明)
public static Class
getRawType(Type type) { if (type instanceof Class
) { // type is a normal class. return (Class
) type; } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // I'm not exactly sure why getRawType() returns Type instead of Class. // Neal isn't either but suspects some pathological case related // to nested classes exists. Type rawType = parameterizedType.getRawType(); checkArgument(rawType instanceof Class); return (Class
) rawType; } else if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType)type).getGenericComponentType(); return Array.newInstance(getRawType(componentType), 0).getClass(); } else if (type instanceof TypeVariable) { // we could use the variable's bounds, but that won't work if there are multiple. // having a raw type that's more general than necessary is okay return Object.class; } else if (type instanceof WildcardType) { return getRawType(((WildcardType) type).getUpperBounds()[0]); } else { String className = type == null ? "null" : type.getClass().getName(); throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <" + type + "> is of type " + className); } }复制代码

两处对比的看,其实type和rawtype很相似,type通过类来包装说明,而rawtype脱去华丽的衣服。type为GenericArrayType的,把衣服一脱,赤身裸体的一看,擦,原来是个array数组,这就是rawtype。

2. TypeAdapter的获取。

public 
TypeAdapter
getAdapter(TypeToken
type) { TypeAdapter
cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type); if (cached != null) { return (TypeAdapter
) cached; } Map
, FutureTypeAdapter
> threadCalls = calls.get(); boolean requiresThreadLocalCleanup = false; if (threadCalls == null) { threadCalls = new HashMap
, FutureTypeAdapter
>(); calls.set(threadCalls); requiresThreadLocalCleanup = true; } // the key and value type parameters always agree FutureTypeAdapter
ongoingCall = (FutureTypeAdapter
) threadCalls.get(type); if (ongoingCall != null) { return ongoingCall; } try { FutureTypeAdapter
call = new FutureTypeAdapter
(); threadCalls.put(type, call); for (TypeAdapterFactory factory : factories) { TypeAdapter
candidate = factory.create(this, type); if (candidate != null) { call.setDelegate(candidate); typeTokenCache.put(type, candidate); return candidate; } } throw new IllegalArgumentException("GSON cannot handle " + type); }复制代码

如果缓存中没有该Type对应TypeAdapter,就创建TypeAdapter。前面提过TypeAdapter是由TypeAdapterFactory创建的,所以有代码:

for (TypeAdapterFactory factory : factories) {        TypeAdapter
candidate = factory.create(this, type); if (candidate != null) { call.setDelegate(candidate); typeTokenCache.put(type, candidate); return candidate; } }复制代码

遍历所有的TypeAdapterFactory,如果该工厂能创建该Type的TypeAdapter就返回该TypeAdapter对象。

那么重点来了,factories这么多的TypeAdapterFactory是怎么来了的?

在我们new Gson的时候,就往factories中塞入了不同类型的TypeAdapterFactory,包括StringTypeAdapterFactory等等,代码如下:

public Gson(xxx)    {        ...        factories.add(TypeAdapters.STRING_FACTORY);        factories.add(TypeAdapters.STRING_FACTORY);        factories.add(TypeAdapters.INTEGER_FACTORY);        factories.add(TypeAdapters.BOOLEAN_FACTORY);        factories.add(TypeAdapters.BYTE_FACTORY);        factories.add(TypeAdapters.SHORT_FACTORY);        ...    }   复制代码

在遍历factories过程中通过create(this,type)方法来生成TypeAdapter。

我们就以第一个STRING_FACTORY为例先进行说明。
public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);复制代码

接着往下看

public static  TypeAdapterFactory newFactory(      final Class type, final TypeAdapter typeAdapter) {    return new TypeAdapterFactory() {      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal      @Override public 
TypeAdapter
create(Gson gson, TypeToken
typeToken) { return typeToken.getRawType() == type ? (TypeAdapter
) typeAdapter : null; } @Override public String toString() { return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]"; } }; }复制代码

STRING_FACTORY = newFactory(String.class, STRING)的时候,STRING就是处理String类型的TypeAdapter,STRING_FACTORY中的create方法就是判断需要处理的类型是不是String类型的,如果是就返回STRING,否则返回null,即该类型不用STRING来处理。

总的来说,在创建Gson的实例对象时,创建TypeAdapterFactory的集合。每种TypeAdapterFactory实例包含能处理的Type类型和Type类型的TypeAdapter,不能处理的Type类型返回的TypeAdapter为null,所以在遍历factories过程中有:

for (TypeAdapterFactory factory : factories) {        TypeAdapter
candidate = factory.create(this, type); if (candidate != null) { ... return candidate; } }复制代码

3. 由TypeAdapter对象解析json字符串

我们回到最初的代码:

TypeToken
typeToken = (TypeToken
)TypeToken.get(typeOfT);TypeAdapter
typeAdapter = getAdapter(typeToken);T object = typeAdapter.read(reader);复制代码

STRING就是处理String类型的TypeAdapter,然后我们看它的read()方法。

public static final TypeAdapter
STRING = new TypeAdapter
() { @Override public String read(JsonReader in) throws IOException { JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); return null; } /* coerce booleans to strings for backwards compatibility */ if (peek == JsonToken.BOOLEAN) { return Boolean.toString(in.nextBoolean()); } return in.nextString(); } ... };复制代码

到这里位置,我们就将gson.fromJson("hello gson",String.class)的String类型“hello gson”返回。


刚刚是只是牛刀小试,我们的主材料来了,看看有多丰盛...

Gson.from("{ "name": "zhangsan", "age": 15, "grade": [ 95, 98 ] }", Student.class)

我们重新走刚刚的流程,看看怎么处理的

Step one : 获取TypeToken

这一步没有什么与众不同

Step Two: TypeAdapter的获取。

factories中包含了很多基本类型的TypeAdapterFactory,同时也包含用户自定义的类型Factory,看源码:

// type adapters for composite and user-defined types            factories.add(new CollectionTypeAdapterFactory(constructorConstructor));    factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));    this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);    factories.add(jsonAdapterFactory);    factories.add(TypeAdapters.ENUM_FACTORY);    factories.add(new ReflectiveTypeAdapterFactory(constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));复制代码

此处我们能匹配上的是ReflectiveTypeAdapterFactory,然后我们看它的create()方法,关键的地方到了!!!

@Override public 
TypeAdapter
create(Gson gson, final TypeToken
type) { Class
raw = type.getRawType(); if (!Object.class.isAssignableFrom(raw)) { return null; // it's a primitive! } ObjectConstructor
constructor = constructorConstructor.get(type); return new Adapter
(constructor, getBoundFields(gson, type, raw)); }复制代码

a. constructorConstructor 获取Student类的构造器

b. getBoundFields()通过反射获取Student每一个字段的的TypeAdapter,并且包装到Map<String, BoundField>中,后面会讲解getBoundFields()的方法。

Step Three 通过TypeAdapter的read()输出对象

@Override public T read(JsonReader in) throws IOException {      if (in.peek() == JsonToken.NULL) {        in.nextNull();        return null;      }      T instance = constructor.construct();      try {        in.beginObject();        while (in.hasNext()) {          String name = in.nextName();          BoundField field = boundFields.get(name);          if (field == null || !field.deserialized) {            in.skipValue();          } else {            field.read(in, instance);          }        }      } catch (IllegalStateException e) {        throw new JsonSyntaxException(e);      } catch (IllegalAccessException e) {        throw new AssertionError(e);      }      in.endObject();      return instance;    }复制代码

到了这一步就似乎海阔天空了,通过传入的构造器创建Student类的实例,在JsonReader进行处理,in.beginObject()相当于跳过“{”,in.endObject()相当于跳过“}”,其中通过in.hasNext()判断是否处理完成。 在in.nextName()读取json字符串中的key值,然后在boundFields根据key获取对应的BoundField ,最后调用BoundField.read(in,instance)去处理细节,即每个字段的映射,我们看一下内部的细节:

new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {      ...      @Override void read(JsonReader reader, Object value)          throws IOException, IllegalAccessException {        Object fieldValue = typeAdapter.read(reader);        if (fieldValue != null || !isPrimitive) {          field.set(value, fieldValue);        }      }          ...    };复制代码

当Filed都处理完成后,instance实例的每一个需要处理的字段都赋值成功,最终将这个对象return出去。


细节说明:

a. getBoundFields()

private Map
getBoundFields(Gson context, TypeToken
type, Class
raw) { Map
result = new LinkedHashMap
(); if (raw.isInterface()) { return result; } Type declaredType = type.getType(); while (raw != Object.class) { Field[] fields = raw.getDeclaredFields(); for (Field field : fields) { boolean serialize = excludeField(field, true); boolean deserialize = excludeField(field, false); if (!serialize && !deserialize) { continue; } field.setAccessible(true); Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); List
fieldNames = getFieldNames(field); BoundField previous = null; for (int i = 0, size = fieldNames.size(); i < size; ++i) { String name = fieldNames.get(i); if (i != 0) serialize = false; // only serialize the default name BoundField boundField = createBoundField(context, field, name, TypeToken.get(fieldType), serialize, deserialize); BoundField replaced = result.put(name, boundField); if (previous == null) previous = replaced; } if (previous != null) { throw new IllegalArgumentException(declaredType + " declares multiple JSON fields named " + previous.name); } } type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass())); raw = type.getRawType(); } return result; }复制代码

遍历Student类的每一个字段,遍历过程中做了两件事情:

  • a. 该字段能否被序列化和反序列化,如果都不行就没有必要处理该字段,主要通过注解和排除器(Excluder)进行判断。
  • b. 对字段进行BoundField的包装。

b. JsonReader.doPeek()

int doPeek() throws IOException {    int peekStack = stack[stackSize - 1];    if (peekStack == JsonScope.EMPTY_ARRAY) {      stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;    } else if (peekStack == JsonScope.NONEMPTY_ARRAY) {      // Look for a comma before the next element.      int c = nextNonWhitespace(true);      switch (c) {      case ']':        return peeked = PEEKED_END_ARRAY;      case ';':        checkLenient(); // fall-through      case ',':        break;      default:        throw syntaxError("Unterminated array");      }    } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {      stack[stackSize - 1] = JsonScope.DANGLING_NAME;      // Look for a comma before the next element.      if (peekStack == JsonScope.NONEMPTY_OBJECT) {        int c = nextNonWhitespace(true);        switch (c) {        case '}':          return peeked = PEEKED_END_OBJECT;        case ';':          checkLenient(); // fall-through        case ',':          break;        default:          throw syntaxError("Unterminated object");        }      }      int c = nextNonWhitespace(true);      switch (c) {      case '"':        return peeked = PEEKED_DOUBLE_QUOTED_NAME;      case '\'':        checkLenient();        return peeked = PEEKED_SINGLE_QUOTED_NAME;      case '}':        if (peekStack != JsonScope.NONEMPTY_OBJECT) {          return peeked = PEEKED_END_OBJECT;        } else {          throw syntaxError("Expected name");        }      default:        checkLenient();        pos--; // Don't consume the first character in an unquoted string.        if (isLiteral((char) c)) {          return peeked = PEEKED_UNQUOTED_NAME;        } else {          throw syntaxError("Expected name");        }      }    } else if (peekStack == JsonScope.DANGLING_NAME) {      stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;      // Look for a colon before the value.      int c = nextNonWhitespace(true);      switch (c) {      case ':':        break;      case '=':        checkLenient();        if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {          pos++;        }        break;      default:        throw syntaxError("Expected ':'");      }    } else if (peekStack == JsonScope.EMPTY_DOCUMENT) {      if (lenient) {        consumeNonExecutePrefix();      }      stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;    } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {      int c = nextNonWhitespace(false);      if (c == -1) {        return peeked = PEEKED_EOF;      } else {        checkLenient();        pos--;      }    } else if (peekStack == JsonScope.CLOSED) {      throw new IllegalStateException("JsonReader is closed");    }    int c = nextNonWhitespace(true);    switch (c) {    case ']':      if (peekStack == JsonScope.EMPTY_ARRAY) {        return peeked = PEEKED_END_ARRAY;      }      // fall-through to handle ",]"    case ';':    case ',':      // In lenient mode, a 0-length literal in an array means 'null'.      if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {        checkLenient();        pos--;        return peeked = PEEKED_NULL;      } else {        throw syntaxError("Unexpected value");      }    case '\'':      checkLenient();      return peeked = PEEKED_SINGLE_QUOTED;    case '"':      return peeked = PEEKED_DOUBLE_QUOTED;    case '[':      return peeked = PEEKED_BEGIN_ARRAY;    case '{':      return peeked = PEEKED_BEGIN_OBJECT;    default:      pos--; // Don't consume the first character in a literal value.    }    int result = peekKeyword();    if (result != PEEKED_NONE) {      return result;    }    result = peekNumber();    if (result != PEEKED_NONE) {      return result;    }    if (!isLiteral(buffer[pos])) {      throw syntaxError("Expected value");    }    checkLenient();    return peeked = PEEKED_UNQUOTED;  }复制代码

该操作逻辑处理较强,主要工作分为3点:

  • json的格式校验,格式不合法抛出异常
  • 根据当前的操作,决定下一步的操作方式
  • 流中下一部分的内容类型

转载于:https://juejin.im/post/5b8a2f5af265da43445f75ee

你可能感兴趣的文章
Linux内存管理之mmap详解 (可用于android底层内存调试)
查看>>
利润表(年末)未分配利润公式备份
查看>>
Android开发中ViewStub的应用方法
查看>>
gen already exists but is not a source folder. Convert to a source folder or rename it 的解决办法...
查看>>
HDOJ-2069Coin Change(母函数加强)
查看>>
遍历Map的四种方法
查看>>
Altium Designer 小记
查看>>
【Linux高级驱动】I2C驱动框架分析
查看>>
赵雅智:js知识点汇总
查看>>
二维有序数组查找数字
查看>>
20个Linux服务器性能调优技巧
查看>>
多重影分身:一套代码如何生成多个小程序?
查看>>
Oracle将NetBeans交给了Apache基金会
查看>>
填坑记:Uncaught RangeError: Maximum call stack size exceeded
查看>>
SpringCloud之消息总线(Spring Cloud Bus)(八)
查看>>
DLA实现跨地域、跨实例的多AnalyticDB读写访问
查看>>
实时编辑
查看>>
KVO原理分析及使用进阶
查看>>
【348天】每日项目总结系列086(2018.01.19)
查看>>
【294天】我爱刷题系列053(2017.11.26)
查看>>