一个容错的Gson新世界

1. 闯入背景:

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

干货地址:类型容错的Gsongit

2. Gson官方库地址:

Github地址github

3. 前提说明

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

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> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    Object object = fromJson(json, (Type) classOfT);
    return Primitives.wrap(classOfT).cast(object);
  }
  
  public <T> 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> 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> 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<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      TypeAdapter<T> 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)bash

1. TypeToken的获取

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

没什么好瞅的~ 看new吧!服务器

TypeToken(Type type) {
    this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
    this.rawType = (Class<? super T>) $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类型进行的类型的从新包装。ide

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

    Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
    boolean requiresThreadLocalCleanup = false;
    if (threadCalls == null) {
      threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
      calls.set(threadCalls);
      requiresThreadLocalCleanup = true;
    }

    // the key and value type parameters always agree
    FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
    if (ongoingCall != null) {
      return ongoingCall;
    }

    try {
      FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
      threadCalls.put(type, call);

      for (TypeAdapterFactory factory : factories) {
        TypeAdapter<T> 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<T> 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 <TT> TypeAdapterFactory newFactory(
      final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
    return new TypeAdapterFactory() {
      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        return typeToken.getRawType() == type ? (TypeAdapter<T>) 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<T> candidate = factory.create(this, type);
        if (candidate != null) {
            ...
          return candidate;
        }
      }
复制代码

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

咱们回到最初的代码:

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

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

public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
    @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 <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
    Class<? super T> raw = type.getRawType();

    if (!Object.class.isAssignableFrom(raw)) {
      return null; // it's a primitive! } ObjectConstructor<T> constructor = constructorConstructor.get(type); return new Adapter<T>(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<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
    Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
    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<String> 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的格式校验,格式不合法抛出异常
  • 根据当前的操做,决定下一步的操做方式
  • 流中下一部分的内容类型
相关文章
相关标签/搜索