闖入背景
公司項目中使用 Gson 框架對服務(wù)器傳過來的 Json 數(shù)據(jù)進(jìn)行解析,而服務(wù)器后臺數(shù)據(jù)很大程度上是通過運營后臺人員配置。由于各種原因運營可能將某一字段類型配置錯誤,比如集合類型配置成字符串類型。雖然業(yè)務(wù)層會進(jìn)行異常的捕獲,但是僅因為一個字段的錯誤,導(dǎo)致整個 Json 數(shù)據(jù)失效,因小失大,甚至可能會造成重大損失,比如直播間禮物墻,因為一個禮物的某一個字段的錯誤,導(dǎo)致整個禮物墻展示為空,在線上環(huán)境這個算是重大事故了。于是,一個對基本類型容錯的 Gson 改造庫的需求油然而生,對于錯誤的數(shù)據(jù)以默認(rèn)值填充。
干貨地址:類型容錯的 Gson
https://github.com/1004145468/IKGson
Gson官方庫地址
Github地址
https://github.com/1004145468/IKGson
前提說明
a. 當(dāng)前分析的 Gson 版本號為 2.8.1。
b. Gson 的處理過程主要分為兩個流向,一個是序列化,將 javabean 對象轉(zhuǎn)化為 json 字符串;另一個是反序列化,將 json 字符串映射成javabean 對象。
c. 這兩個流向處理前都有一個共同的操作,從傳入的 java 實例對象或者字節(jié)碼對象中獲取 TypeAdapter,對于序列化就通過 Jsonwriter 進(jìn)行寫,對于反序列化就通過 JsonReader 進(jìn)行讀,所以此篇只分析 Gson 讀的過程,寫處理操作流程一樣。
Gson 關(guān)鍵列的梳理
Gson 開發(fā)者直接使用的類,只對輸入和輸出負(fù)責(zé)。
TypeToken 封裝“操作類”(Gson.fromJson(json,People.class、Gson.toJson(new People)) 兩處的 People 都是操作類)的類型。
TypeAdapter 直接操作序列化與反序列化的過程,所以該抽象類中存在 read() 和 write 方法。
TypeAdapterFactory 用于生產(chǎn) TypeAdapter 的工廠類。
GsonReader 和 GsonWriter是 Gson 處理內(nèi)容的包裝流,核心的操作有:
peek() 流中下一個需要處理的內(nèi)容
nextName() 讀取 json 的 key
nextString() 讀取一個 String 類型的 value
nextInt() 讀取一個 String 類型的 value
nextBoolean() 讀取一個 Boolean 類型的 value
...
源碼分析
從 Gson.from(json, People.class) 突入
fromJson(json,Peolple.class)的調(diào)用鏈
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 對象的獲取
根據(jù) TypeToken 獲取 TypeAdapter 對象
由 TypeAdapter 對象解析 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 的獲?。╰ype 的華麗包裝)
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;
}
進(jìn)入條件的篩選,第一個if還是好理解,后面的是什么鬼? 不用著急,待我給施主梳理,之前 Gson.from(json, People.class)的調(diào)用鏈中有一個 fromJson(Reader json, Type typeOfT)
,用戶使用時的切入點如果是它就可能是篩選情況的其他條件,此返回的 type 相對于對傳入的java類型進(jìn)行的類型的重新包裝。
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 對應(yīng) 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 為例先進(jìn)行說明。
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()方法,關(guān)鍵的地方到了?。?!
@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 類的構(gòu)造器
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;
}
到了這一步就似乎海闊天空了,通過傳入的構(gòu)造器創(chuàng)建 Student 類的實例,在 JsonReader 進(jìn)行處理,in.beginObject() 相當(dāng)于跳過“{”,in.endObject()相當(dāng)于跳過“}”,其中通過in.hasNext()判斷是否處理完成。
在 in.nextName() 讀取 json 字符串中的 key 值,然后在boundFields 根據(jù)key獲取對應(yīng)的 BoundField ,最后調(diào)用BoundField.read(in,instance) 去處理細(xì)節(jié),即每個字段的映射,我們看一下內(nèi)部的細(xì)節(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);
}
}
...
};
當(dāng) Filed 都處理完成后,instance 實例的每一個需要處理的字段都賦值成功,最終將這個對象 return出去。
細(xì)節(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類的每一個字段,遍歷過程中做了兩件事情:
a. 該字段能否被序列化和反序列化,如果都不行就沒有必要處理該字段,主要通過注解和排除器(Excluder)進(jìn)行判斷。
b. 對字段進(jìn)行 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;
}
該操作邏輯處理較強(qiáng),主要工作分為 3 點:
json 的格式校驗,格式不合法拋出異常
根據(jù)當(dāng)前的操作,決定下一步的操作方式
流中下一部分的內(nèi)容類型
與之相關(guān)