簡介
Gson是google提供的一款Json解析框架,基本很多項(xiàng)目都會使用到官套。Gson自帶的容錯機(jī)制能夠使解析過程更加友好酒奶,但是并不能幫助我們解決所以的容錯問題蚁孔,這時候可以通過向Gson注冊自己的解析適配器來接管Gson的解析過程。下面將通過分析源碼的方式惋嚎,了解Gson內(nèi)部實(shí)現(xiàn)原理和解析流程杠氢。帶大家徹底搞懂Gson
重點(diǎn)
- 擴(kuò)展Gson容錯機(jī)制
- Gson注解使用
- Gson解析流程
場景
- 在解析Json數(shù)據(jù)的時候,由于后臺返回的json數(shù)據(jù)格式的不規(guī)范另伍,偶現(xiàn)解析數(shù)據(jù)崩潰鼻百,當(dāng)然Gson自身就有一定的容錯機(jī)制,但是摆尝,有些時候并不能到達(dá)項(xiàng)目的需要温艇。比如:Gson對int數(shù)據(jù)的解析,當(dāng)后臺返回"123"和""時堕汞,前者由于Gson自身的容錯處理能夠正常解析勺爱,但是后者卻會導(dǎo)致應(yīng)用崩潰,其實(shí)我們更希望將""解析成“0”而不是應(yīng)用崩潰讯检。
- 有時候琐鲁,由于后臺返回的json數(shù)據(jù)中某個字段的名字不一樣,導(dǎo)致我們不得不在數(shù)據(jù)實(shí)體里面新加字段或者新創(chuàng)建一個實(shí)體類人灼,這樣不但會增加多余的代碼围段,同時讓代碼邏輯變得更加混亂,后期難以維護(hù)挡毅。
- 配置某些字段在序列化和反序列化過程中的行為蒜撮。
fromJson(反序列化) and toJson(序列化)
這兩個方法最重要的地方都是,獲取一個TypeAdapter對象跪呈,調(diào)用read和write完成反序列化和序列化過程。完整代碼查看 getAdapter(TypeToken<T> type)方法取逾。
TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
if (cached != null) {
return (TypeAdapter<T>) cached;
}
從緩存獲取TypeAdapter對象耗绿,存在者直接返回
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
通過ThreadLocal緩存TypeAdapter對象,不同的線程使用緩存來解析的時候互不影響砾隅。
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;
}
}
如果不存在緩存误阻,那么從factories列表里查找,factories是在創(chuàng)建Gson對象時初始化晴埂,添加了很多用于創(chuàng)建TypeAdapter對象的TypeAdapterFactory究反。
- fromJson(反序列化)
實(shí)例:
private fun test(){
val data = "{" +
"\"errcode\": \"\"," +
"\"errmsg\": \"success\"" +
"}"
val item = new Gson().fromJson(data, GsonItem::class.java)
}
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
...
reader.peek();
...
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
T object = typeAdapter.read(reader);
return object;
...
}
- reader.peek()
解析字符串第一個字符在json格式里的類型。 - getAdapter(typeToken)
通過getAdapter(TypeToken<T> type)方法獲取TypeAdapter對象儒洛,分兩種情況:- 類使用了@JsonAdapter
看一下Gson初始化“factories”數(shù)組時的順序精耐,添加JsonAdapterAnnotationTypeAdapterFactory對象在ReflectiveTypeAdapterFactory對象之前±哦停看一下create方法:
如果對實(shí)體類使用了@JsonAdapter且指定的適配器存在那么就會返回@JsonAdapter里指定的適配器而不返回ReflectiveTypeAdapterFactory創(chuàng)建的卦停,這樣我們就可以自己接管后面的解析過程了向胡,具體用法參考后面給出的工程源碼。@SuppressWarnings("unchecked") @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) { Class<? super T> rawType = targetType.getRawType(); JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class); if (annotation == null) { return null; } return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation); }
- 沒有使用@JsonAdapter注解:
這里要注意惊完,對于基礎(chǔ)知識不太牢固的人僵芹,可能會認(rèn)為這里返回的是ObjectTypeAdapter實(shí)例,認(rèn)為所以類都都繼承于Object小槐,所以GsonItem.class == Object.class為true拇派,其實(shí)是不等的,這里返回的應(yīng)該是ReflectiveTypeAdapterFactory實(shí)例凿跳,調(diào)用ReflectiveTypeAdapterFactory里的create返回內(nèi)部Adapter對象件豌。
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) { ... // constructorConstructor = new ConstructorConstructor(instanceCreators); ObjectConstructor<T> constructor = constructorConstructor.get(type); return new Adapter<T>(constructor, getBoundFields(gson, type, raw)); }
-
getBoundFields(gson, type, raw)
將實(shí)體類中需要解析的字段添加一個集合里,在反序列化時進(jìn)行賦值拄显。- 得到實(shí)體類所以的字段
Field[] fields = raw.getDeclaredFields();
- 字段是否參與反序列化或者序列化過程
boolean serialize = excludeField(field, true); boolean deserialize = excludeField(field, false);
static boolean excludeField(Field f, boolean serialize, Excluder excluder) { return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize); }
3.excludeClassChecks(clazz)檢查class類型是否符合序列化或者反序列化要求苟径,這里可以自己點(diǎn)擊去看一下。里面用到的Since和Until注解躬审,作用于類棘街,和作用于字段意思一樣,將在下面講解承边。
- excludeClassInStrategy(clazz, serialize)通過加入自己的策略來控制字段是否要參與解析遭殉,在初始化的時候可以加入自己的策略。如果某個字段不符合Gson解析要求博助,但是你覺得可以正常解析险污,那么就可以在自己的策略返回true。
private boolean excludeClassInStrategy(Class<?> clazz, boolean serialize) { List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies; for (ExclusionStrategy exclusionStrategy : list) { if (exclusionStrategy.shouldSkipClass(clazz)) { return true; } } return false; }
-
excluder.excludeField(f, serialize)過濾字段
@Since / @Until
在配置了new GsonBuilder().setVersion(double v)時富岳,@Since(double v)蛔糯、@Until(double v)才起作用。這個查看源碼可以得知窖式。
查看isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))方法蚁飒,可以得出這兩個注解的用法如下:
比如:/**該屬性自2.2+版本開始棄用*/ @Until(2.2) private String sex; /**該屬性自1.3+版本 開始啟用*/ @Since(1.3) private String name; /**該屬性自1.4+版本開始棄用*/ @Until(1.4) private String number;
@Expose
是否將字段暴露出去,參與序列化和反序列化萝喘。需要 GsonBuilder 配合 .excludeFieldsWithoutExposeAnnotation() 方法使用淮逻,否則不起作用。
if (requireExpose) { Expose annotation = field.getAnnotation(Expose.class); if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) { return true; } }
返回true表示不解析該字段阁簸。這個注解使用時爬早,請注意看這里的判斷邏輯,不然很可能發(fā)現(xiàn)根本解析不出數(shù)據(jù)來启妹。
過濾策略
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies; if (!list.isEmpty()) { FieldAttributes fieldAttributes = new FieldAttributes(field); for (ExclusionStrategy exclusionStrategy : list) { if (exclusionStrategy.shouldSkipField(fieldAttributes)) { return true; } } }
- 得到實(shí)體類所以的字段
-
獲取字段類型
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
-
獲取字段名字
List<String> fieldNames = getFieldNames(field)
@SerializedName
@SerializedName 可以用來配置 JSON 字段的名字筛严,比如:在同一個 Test 對象中的用戶電話,現(xiàn)在不同的接口返回不同的字段翅溺,比如: phone脑漫、user_phone髓抑、userphone,這種差異也可以用 @SerializedName .來解決优幸。
class Test{ @SerializedName("user_phone") var userPhone :String? = null var sex = 0 }
在 @SerializedName 中吨拍,還有一個 alternate 字段,可以對同一個字段配置多個解析名稱网杆。
class Test{ @SerializedName(value = "user_phone",alternate = arrayOf("phone","userphone")) var userPhone :String? = null var sex = 0 }
一旦使用@SerializedName后羹饰,字段本身的名字不在起作用,所以需要指定@SerializedName中value的值碳却。
-
createBoundField(...)
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
是否是基本數(shù)據(jù)類型
@JsonAdapter
Gson的使用者可以根據(jù)實(shí)際的需要對某個具體類型的序列化和反序列化的轉(zhuǎn)換進(jìn)行控制队秩,可放置在屬性上。
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class); TypeAdapter<?> mapped = null; if (annotation != null) { mapped = jsonAdapterFactory.getTypeAdapter( constructorConstructor, context, fieldType, annotation); }
如果實(shí)體類某屬性使用了@JsonAdapter昼浦,那么該屬性的序列化和反序列化將由指定的適配器接管馍资。如果沒有這會從Gson初始化中查找對于的解析適配器。
- 類使用了@JsonAdapter
- typeAdapter.read(reader)
- 創(chuàng)建實(shí)體類對象
具體怎樣創(chuàng)建的关噪,請查看源碼鸟蟹,也比較簡單,這個過程也是可以通過擴(kuò)展相關(guān)類來接管的使兔。T instance = constructor.construct();
- Json流開始的類型建钥,并做上相應(yīng)標(biāo)記。
in.beginObject();
- 讀值
獲取Json數(shù)據(jù)中的name虐沥,如果在 boundFields(需要反序列化的字段)沒有者跳過熊经,如果有,者讀取對應(yīng)值并賦值給實(shí)體類對應(yīng)字段欲险。String name = in.nextName(); BoundField field = boundFields.get(name); if (field == null || !field.deserialized) { in.skipValue(); } else { field.read(in, instance); }
- field.read(in, instance)
讀取值并賦值給實(shí)體類镐依,至于怎么讀取的,可以看一下Gson初始化里面天试,已經(jīng)添加的解析適配器馋吗。@Override void read(JsonReader reader, Object value) throws IOException, IllegalAccessException { Object fieldValue = typeAdapter.read(reader); if (fieldValue != null || !isPrimitive) { field.set(value, fieldValue); } }
- field.read(in, instance)
- 創(chuàng)建實(shí)體類對象
- toJson(序列化)
基本流程和大部分實(shí)現(xiàn)都和fromJson(反序列化)相同,請自行查看源碼。 - 補(bǔ)充
- gson = new GsonBuilder().setDateFormat("yyyy-MM").create()可以設(shè)置日期類型在序列化和反序列化過程輸出的格式秋秤。Gson提供了DefaultDateTypeAdapter和DateTypeAdapter來進(jìn)行轉(zhuǎn)換。
- GsonBuilder()
.registerTypeAdapter(Int::class.java, IntDeserializerAdapter())
. registerTypeHierarchyAdapter(String::class.java, NumberTypeAdapter())
.create()
.fromJson<GsonItem>(jsonStr,GsonItem::class.java)
注冊自己的解析適配器脚翘,代替Gson自帶的灼卢。
Gson能夠把類似"123"解析成Int,但是如果是""者會拋異常導(dǎo)致崩潰来农,所以我們可以接管反序列化過程鞋真,當(dāng)出現(xiàn)異常時候返回0。override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Int = try { json!!.asInt } catch (e: NumberFormatException) { 0 }
- registerTypeAdapter() 和registerTypeHierarchyAdapter()區(qū)別
前者要求我們傳遞一個明確的類型沃于,也就是說它不支持繼承涩咖,而 后者 則可以支持繼承海诲。
- 對補(bǔ)充中的第二點(diǎn)補(bǔ)充
使用GsonBuilder()
.registerTypeAdapter()或者GsonBuilder(...)
.registerTypeHierarchyAdapter(...)注冊的適配器是繼承于TypeAdapter而不是JsonDeserializer,你發(fā)現(xiàn)怎么try...catch應(yīng)用都會崩潰檩互。這里看一下有什么不同特幔,找到TreeTypeAdapter類的read方法,至于為什么是這個類闸昨,請按照這篇文章邏輯梳理一遍蚯斯。
@Override public T read(JsonReader in) throws IOException {
if (deserializer == null) {
return delegate().read(in);
}
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
return null;
}
return deserializer.deserialize(value, typeToken.getType(), context);
}
如果deserializer不等于null,這反序列化過程由繼承于JsonDeserializer的類接管饵较。那么看一下為什么繼承TypeAdapter會有問題拍嵌,定位到自定義的TypeAdapter的read方法
override fun read(i: JsonReader): Int? {
if (i.peek() == JsonToken.NULL) {
i.nextNull()
return 0
}
try {
return i.nextInt()
} catch (e: Exception) {
return 0
}
}
看一下i.nextInt()
public int nextInt() throws IOException {
...
try {
result = Integer.parseInt(peekedString);
peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++;
return result;
} catch (NumberFormatException ignored) {
}
} else {
throw new IllegalStateException("Expected an int but was " + peek() + locationString());
}
...
peeked = PEEKED_BUFFERED;
...
如果 Integer.parseInt(peekedString)出現(xiàn)異常,那么peeked = PEEKED_BUFFERED;由于try...catch循诉,所以不會崩潰横辆。
接下來獲取下一個json數(shù)據(jù)中的name,定位到ReflectiveTypeAdapterFactory中的read方法里的String name = in.nextName()方法茄猫,當(dāng)peeked = PEEKED_BUFFERED拋出異常狈蚤,導(dǎo)致程序崩潰,解決辦法就是自己寫解析流程而不是簡單的try...catch募疮。
更多用法請查看下面工程代碼
SimpleGson