公司項目中使用 Gson 框架對服務器傳過來的 Json 數(shù)據(jù)進行解析,而服務器后臺數(shù)據(jù)很大程度上是通過運營后臺人員配置。由于各種原因運營可能將某一字段類型配置錯誤,比如集合類型配置成字符串類型。雖然業(yè)務層會進行異常的捕獲,但是僅因為一個字段的錯誤,導致整個 Json 數(shù)據(jù)失效,因小失大,甚至可能會造成重大損失,比如直播間禮物墻,因為一個禮物的某一個字段的錯誤,導致整個禮物墻展示為空,在線上環(huán)境這個算是重大事故了。于是,一個對基本類型容錯的 Gson 改造庫的需求油然而生,對于錯誤的數(shù)據(jù)以默認值填充。
干貨地址:類型容錯的 Gson
https://github.com/1004145468/IKGson
https://github.com/1004145468/IKGson a. 當前分析的 Gson 版本號為 2.8.1。 b. Gson 的處理過程主要分為兩個流向,一個是序列化,將 javabean 對象轉化為 json 字符串;另一個是反序列化,將 json 字符串映射成javabean 對象。 c. 這兩個流向處理前都有一個共同的操作,從傳入的 java 實例對象或者字節(jié)碼對象中獲取 TypeAdapter,對于序列化就通過 Jsonwriter 進行寫,對于反序列化就通過 JsonReader 進行讀,所以此篇只分析 Gson 讀的過程,寫處理操作流程一樣。
Gson 開發(fā)者直接使用的類,只對輸入和輸出負責。 TypeToken 封裝“操作類”(Gson.fromJson(json,People.class、Gson.toJson(new People)) 兩處的 People 都是操作類)的類型。 TypeAdapter 直接操作序列化與反序列化的過程,所以該抽象類中存在 read() 和 write 方法。 TypeAdapterFactory 用于生產(chǎn) 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 的解析過程分三步過程: 根據(jù)以上的三步,我們逐一突破 我們先從簡單的入手,請記住我們的例子: gson.fromJson("hello gson",String.class)
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 的獲取流程
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類型進行的類型的重新包裝。
rawType 的獲?。╰ype 的簡單粗暴說明)
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 數(shù)組,這就是 rawtype。
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,就創(chuàng)建 TypeAdapter。前面提過 TypeAdapter是由TypeAdapterFactory創(chuàng)建的,所以有代碼:
for (TypeAdapterFactory factory : factories) { TypeAdapter<T> candidate = factory.create(this, type); if (candidate != null) { call.setDelegate(candidate); typeTokenCache.put(type, candidate); return candidate; } }
遍歷所有的 TypeAdapterFactory,如果該工廠能創(chuàng)建該 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 來處理。 總的來說,在創(chuàng)建 Gson 的實例對象時,創(chuàng)建 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; } }
由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 這一步?jīng)]有什么與眾不同
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; }
到了這一步就似乎海闊天空了,通過傳入的構造器創(chuàng)建 Student 類的實例,在 JsonReader 進行處理,in.beginObject() 相當于跳過“{”,in.endObject()相當于跳過“}”,其中通過in.hasNext()判斷是否處理完成。 在 in.nextName() 讀取 json 字符串中的 key 值,然后在boundFields 根據(jù)key獲取對應的 BoundField ,最后調用BoundField.read(in,instance) 去處理細節(jié),即每個字段的映射,我們看一下內部的細節(jié):
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出去。 細節(jié)說明:
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類的每一個字段,遍歷過程中做了兩件事情:
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 的格式校驗,格式不合法拋出異常 根據(jù)當前的操作,決定下一步的操作方式 流中下一部分的內容類型
|