1. 闖入背景:
公司專案中使用Gson框架對伺服器傳過來的Json資料進行解析,而伺服器後臺資料很大程度上是通過運營後臺人員配置。由於各種原因運營可能將某一欄位型別配置錯誤,比如集合型別配置成字串型別。雖然業務層會進行異常的捕獲,但是僅因為一個欄位的錯誤,導致整個Json資料失效,因小失大,甚至可能會造成重大損失,比如直播間禮物牆,因為一個禮物的某一個欄位的錯誤,導致整個禮物牆展示為空,線上上環境這個算是重大事故了。於是,一個對基本型別容錯的Gson改造庫的需求油然而生,對於錯誤的資料以預設值填充。
乾貨地址:型別容錯的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> 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型別進行的型別的重新包裝。
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的格式校驗,格式不合法丟擲異常
- 根據當前的操作,決定下一步的操作方式
- 流中下一部分的內容型別