打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
一个容错的 Gson 新世界

闯入背景

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

干货地址:类型容错的 Gson

https://github.com/1004145468/IKGson

Gson官方库地址

Github地址

https://github.com/1004145468/IKGson

前提说明

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

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

  • ...

源码分析

从 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)

  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类型进行的类型的重新包装。

  1. 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。

  1. 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 + "]";
     }
   };
 }

STRINGFACTORY = newFactory(String.class, STRING) 的时候,STRING 就是处理 String 类型的 TypeAdapter,STRINGFACTORY中的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;
       }
     }
  1. 由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 
中,后面会讲解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 的格式校验,格式不合法抛出异常

  • 根据当前的操作,决定下一步的操作方式

  • 流中下一部分的内容类型

与之相关

Android 彻底组件化 demo 发布

使用 Dagger2 前你必须了解的一些设计原则

Android 组件化 —— 路由设计最佳实践

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
Gson User Guide
gson使用
[转]用GSON 五招之内搞定任何JSON数组
你真的会用Gson吗?Gson使用指南(一)
GSON使用笔记(3)
java – 如何将JSON对象解析为`Map>`
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服