Gson使用指南

轉(zhuǎn)自:http://www.lxweimin.com/p/3108f1e44155?nomobile=yes


該系列其它文章

你真的會(huì)用Gson嗎?Gson使用指南(一)

你真的會(huì)用Gson嗎?Gson使用指南(二)

你真的會(huì)用Gson嗎?Gson使用指南(三)

你真的會(huì)用Gson嗎?Gson使用指南(四)

注:此系列基于Gson 2.4。

本次文章的主要內(nèi)容:

TypeAdapter

JsonSerializer與JsonDeserializer

TypeAdapterFactory

@JsonAdapter注解

TypeAdapter與 JsonSerializer、JsonDeserializer對(duì)比

TypeAdapter實(shí)例

結(jié)語(yǔ)

后期預(yù)告

一、TypeAdapter

TypeAdapter是Gson自2.0(源碼注釋上說(shuō)的是2.1)開(kāi)始版本提供的一個(gè)抽象類(lèi),用于接管某種類(lèi)型的序列化和反序列化過(guò)程,包含兩個(gè)注要方法write(JsonWriter,T)和read(JsonReader)其它的方法都是final方法并最終調(diào)用這兩個(gè)抽象方法。

publicabstractclassTypeAdapter{publicabstractvoidwrite(JsonWriter out, T value)throwsIOException;publicabstractTread(JsonReader in)throwsIOException;//其它final 方法就不貼出來(lái)了,包括`toJson`、`toJsonTree`、`toJson`和`nullSafe`方法。}

注意:TypeAdapter 以及 JsonSerializer 和 JsonDeserializer 都需要與GsonBuilder.registerTypeAdapter示或GsonBuilder.registerTypeHierarchyAdapter配合使用,下面將不再重復(fù)說(shuō)明。

使用示例:

User user =newUser("怪盜kidou",24);user.emailAddress ="ikidou@example.com";Gson gson =newGsonBuilder()//為User注冊(cè)TypeAdapter.registerTypeAdapter(User.class,newUserTypeAdapter())? ? ? ? .create();System.out.println(gson.toJson(user));

UserTypeAdapter的定義:

publicclassUserTypeAdapterextendsTypeAdapter{@Overridepublicvoidwrite(JsonWriter out, User value)throwsIOException{? ? ? ? out.beginObject();? ? ? ? out.name("name").value(value.name);? ? ? ? out.name("age").value(value.age);? ? ? ? out.name("email").value(value.email);? ? ? ? out.endObject();? ? }@OverridepublicUserread(JsonReader in)throwsIOException{? ? ? ? User user =newUser();? ? ? ? in.beginObject();while(in.hasNext()) {switch(in.nextName()) {case"name":? ? ? ? ? ? ? ? ? ? user.name = in.nextString();break;case"age":? ? ? ? ? ? ? ? ? ? user.age = in.nextInt();break;case"email":case"email_address":case"emailAddress":? ? ? ? ? ? ? ? ? ? user.email = in.nextString();break;? ? ? ? ? ? }? ? ? ? }? ? ? ? in.endObject();returnuser;? ? }}

當(dāng)我們?yōu)閁ser.class注冊(cè)了TypeAdapter之后,只要是操作User.class那些之前介紹的@SerializedName、FieldNamingStrategy、Since、Until、Expos通通都黯然失色,失去了效果,只會(huì)調(diào)用我們實(shí)現(xiàn)的UserTypeAdapter.write(JsonWriter, User)方法,我想怎么寫(xiě)就怎么寫(xiě)。

再說(shuō)一個(gè)場(chǎng)景,在該系列的第一篇文章就說(shuō)到了Gson有一定的容錯(cuò)機(jī)制,比如將字符串"24"轉(zhuǎn)成int 的24,但如果有些情況下給你返了個(gè)空字符串怎么辦(有人給我評(píng)論問(wèn)到這個(gè)問(wèn)題)?雖然這是服務(wù)器端的問(wèn)題,但這里我們只是做一個(gè)示范。

int型會(huì)出錯(cuò)是吧,根據(jù)我們上面介紹的,我注冊(cè)一個(gè)TypeAdapter 把 序列化和反序列化的過(guò)程接管不就行了?

Gson gson =newGsonBuilder()? ? ? ? .registerTypeAdapter(Integer.class,newTypeAdapter() {@Overridepublicvoidwrite(JsonWriter out, Integer value)throwsIOException{? ? ? ? ? ? ? ? out.value(String.valueOf(value));? ? ? ? ? ? }@OverridepublicIntegerread(JsonReader in)throwsIOException{try{returnInteger.parseInt(in.nextString());? ? ? ? ? ? ? ? }catch(NumberFormatException e) {return-1;? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? })? ? ? ? .create();System.out.println(gson.toJson(100));// 結(jié)果:"100"System.out.println(gson.fromJson("\"\"",Integer.class));// 結(jié)果:-1

注:測(cè)試空串的時(shí)候一定是"\"\""而不是"",""代表的是沒(méi)有json串,"\"\""才代表json里的""。

你說(shuō)這一接管就要管兩樣好麻煩呀,我明明只想管序列化(或反列化)的過(guò)程的,另一個(gè)過(guò)程我并不關(guān)心,難道沒(méi)有其它更簡(jiǎn)單的方法么? 當(dāng)然有!就是接下來(lái)要介紹的JsonSerializer與JsonDeserializer

二、JsonSerializer與JsonDeserializer

JsonSerializer和JsonDeserializer不用像TypeAdapter一樣,必須要實(shí)現(xiàn)序列化和反序列化的過(guò)程,你可以據(jù)需要選擇,如只接管序列化的過(guò)程就用JsonSerializer,只接管反序列化的過(guò)程就用JsonDeserializer,如上面的需求可以用下面的代碼。

Gson gson =newGsonBuilder()? ? ? ? .registerTypeAdapter(Integer.class,newJsonDeserializer() {? ? ? ? ? ? @OverridepublicIntegerdeserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)throws JsonParseException{try{returnjson.getAsInt();? ? ? ? ? ? ? ? }catch(NumberFormatException e) {return-1;? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? })? ? ? ? .create();System.out.println(gson.toJson(100));//結(jié)果:100System.out.println(gson.fromJson("\"\"", Integer.class));//結(jié)果-1

下面是所有數(shù)字都轉(zhuǎn)成序列化為字符串的例子

JsonSerializer numberJsonSerializer =newJsonSerializer() {@OverridepublicJsonElementserialize(Number src, Type typeOfSrc, JsonSerializationContext context){returnnewJsonPrimitive(String.valueOf(src));? ? }};Gson gson =newGsonBuilder()? ? ? ? .registerTypeAdapter(Integer.class, numberJsonSerializer)? ? ? ? .registerTypeAdapter(Long.class, numberJsonSerializer)? ? ? ? .registerTypeAdapter(Float.class, numberJsonSerializer)? ? ? ? .registerTypeAdapter(Double.class, numberJsonSerializer)? ? ? ? .create();System.out.println(gson.toJson(100.0f));//結(jié)果:"100.0"

注:registerTypeAdapter必須使用包裝類(lèi)型,所以int.class,long.class,float.class和double.class是行不通的。同時(shí)不能使用父類(lèi)來(lái)替上面的子類(lèi)型,這也是為什么要分別注冊(cè)而不直接使用Number.class的原因。

上面特別說(shuō)明了registerTypeAdapter不行,那就是有其它方法可行咯?當(dāng)然!換成registerTypeHierarchyAdapter就可以使用Number.class而不用一個(gè)一個(gè)的當(dāng)獨(dú)注冊(cè)啦!

registerTypeAdapter與registerTypeHierarchyAdapter的區(qū)別:

registerTypeAdapterregisterTypeHierarchyAdapter

支持泛型是否

支持繼承否是

注:如果一個(gè)被序列化的對(duì)象本身就帶有泛型,且注冊(cè)了相應(yīng)的TypeAdapter,那么必須調(diào)用Gson.toJson(Object,Type),明確告訴Gson對(duì)象的類(lèi)型。

Type type =newTypeToken>() {}.getType();TypeAdapter typeAdapter =newTypeAdapter>() {//略};Gson gson =newGsonBuilder()? ? ? ? .registerTypeAdapter(type, typeAdapter)? ? ? ? .create();List list =newArrayList<>();list.add(newUser("a",11));list.add(newUser("b",22));//注意,多了個(gè)type參數(shù)String result = gson.toJson(list, type);

三、TypeAdapterFactory

TypeAdapterFactory,見(jiàn)名知意,用于創(chuàng)建TypeAdapter的工廠類(lèi),通過(guò)對(duì)比Type,確定有沒(méi)有對(duì)應(yīng)的TypeAdapter,沒(méi)有就返回null,與GsonBuilder.registerTypeAdapterFactory配合使用。

Gsongson =newGsonBuilder()? ? .registerTypeAdapterFactory(newTypeAdapterFactory() {@Overridepublic TypeAdapter create(Gsongson,TypeTokentype) {returnnull;? ? ? ? }? ? })? ? .create();

四、@JsonAdapter注解

JsonAdapter相較之前介紹的SerializedName、FieldNamingStrategy、Since、Until、Expos這幾個(gè)注解都是比較特殊的,其它的幾個(gè)都是用在POJO的字段上,而這一個(gè)是用在POJO類(lèi)上的,接收一個(gè)參數(shù),且必須是TypeAdpater,JsonSerializer或JsonDeserializer這三個(gè)其中之一。

上面說(shuō)JsonSerializer和JsonDeserializer都要配合GsonBuilder.registerTypeAdapter使用,但每次使用都要注冊(cè)也太麻煩了,JsonAdapter就是為了解決這個(gè)痛點(diǎn)的。

使用方法(以User為例):

@JsonAdapter(UserTypeAdapter.class)//加在類(lèi)上publicclassUser{publicUser(){? ? }publicUser(String name,intage){this.name = name;this.age = age;? ? }publicUser(String name,intage, String email){this.name = name;this.age = age;this.email = email;? ? }publicString name;publicintage;@SerializedName(value ="emailAddress")publicString email;}

使用時(shí)不用再使用GsonBuilder去注冊(cè)UserTypeAdapter了。

注:@JsonAdapter僅支持TypeAdapter或TypeAdapterFactory

Gson gson =newGson();User user =newUser("怪盜kidou",24,"ikidou@example.com");System.out.println(gson.toJson(user));//結(jié)果:{"name":"怪盜kidou","age":24,"email":"ikidou@example.com"}//為區(qū)別結(jié)果,特意把email字段與@SerializedName注解中設(shè)置的不一樣

注意:JsonAdapter的優(yōu)先級(jí)比GsonBuilder.registerTypeAdapter的優(yōu)先級(jí)更高。

五、TypeAdapter與 JsonSerializer、JsonDeserializer對(duì)比

TypeAdapterJsonSerializer、JsonDeserializer

引入版本2.01.x

Stream API支持不支持*,需要提前生成JsonElement

內(nèi)存占用小比TypeAdapter大

效率高比TypeAdapter低

作用范圍序列化反序列化序列化反序列化

六、TypeAdapter實(shí)例

注:這里的TypeAdapter泛指TypeAdapter、JsonSerializer和JsonDeserializer。

這里的TypeAdapter 上面講了一個(gè)自動(dòng)將 字符串形式的數(shù)值轉(zhuǎn)換成int型時(shí)可能出現(xiàn) 空字符串的問(wèn)題,下面介紹一個(gè)其它讀者的需求:

服務(wù)器返回的數(shù)據(jù)中data字段類(lèi)型不固定,比如請(qǐng)求成功data是一個(gè)List,不成功的時(shí)候是String類(lèi)型,這樣前端在使用泛型解析的時(shí)候,怎么去處理呢?

其實(shí)這個(gè)問(wèn)題的原因主要由服務(wù)器端造成的,接口設(shè)計(jì)時(shí)沒(méi)有沒(méi)有保證數(shù)據(jù)的一致性,正確的數(shù)據(jù)返回姿勢(shì):同一個(gè)接口任何情況下不得改變返回類(lèi)型,要么就不要返,要么就返空值,如null、[],{}

但這里還是給出解決方案:

方案一:

Gson gson =newGsonBuilder().registerTypeHierarchyAdapter(List.class,newJsonDeserializer>() {? ? @OverridepublicList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {if(json.isJsonArray()){//這里要自己負(fù)責(zé)解析了Gson newGson =newGson();returnnewGson.fromJson(json,typeOfT);? ? ? ? }else{//和接口類(lèi)型不符,返回空ListreturnCollections.EMPTY_LIST;? ? ? ? }? ? }}).create();

方案二:

Gson gson =newGsonBuilder().registerTypeHierarchyAdapter(List.class,newJsonDeserializer>() {? ? @OverridepublicList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {if(json.isJsonArray()) {? ? ? ? ? ? JsonArrayarray= json.getAsJsonArray();? ? ? ? ? ? Type itemType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];Listlist=newArrayList<>();for(int i =0; i

要注意的點(diǎn):

必須使用registerTypeHierarchyAdapter方法,不然對(duì)List的子類(lèi)無(wú)效,但如果POJO中都是使用List,那么可以使用registerTypeAdapter。

對(duì)于是數(shù)組的情況,需要?jiǎng)?chuàng)建一個(gè)新的Gson,不可以直接使用context,不然gson又會(huì)調(diào)我們自定義的JsonDeserializer造成遞歸調(diào)用,方案二沒(méi)有重新創(chuàng)建Gson,那么就需要提取出List中E的類(lèi)型,然后分別反序列化適合為E手動(dòng)注冊(cè)了TypeAdaper的情況。

從效率上推薦方案二,免去重新實(shí)例化Gson和注冊(cè)其它TypeAdapter的過(guò)程。

結(jié)語(yǔ)

Gson系列總算是完成了,感覺(jué)寫(xiě)得越來(lái)越差了,我怕我寫(xiě)得太啰嗦,也不能總是大片大片的貼代碼,所以可能有的地方寫(xiě)得并不詳細(xì),排版也不美觀,但都些都不重點(diǎn),重點(diǎn)是Gson里我們能用上的都一一介紹一遍,只要你確確實(shí)實(shí)把我這幾篇文章上的內(nèi)容都學(xué)會(huì)的話,以后Gson上的任何問(wèn)題都不再是問(wèn)題,當(dāng)然可能很多內(nèi)容對(duì)于實(shí)際的開(kāi)發(fā)中用的并不多,但下次有什么疑難雜癥就難不倒你了。

本系列不提供Demo源碼,最重要的是自己實(shí)驗(yàn)。

寫(xiě)一篇文章還是要花不少時(shí)間和精力,要寫(xiě)示例、調(diào)式、組織語(yǔ)言、碼字等等,加上關(guān)注的人在慢慢的增加的同時(shí)既給了我動(dòng)力也給我不少壓力,如有紕漏或者更好的例子都可以和我交流。

后期預(yù)告:

之前有人給我評(píng)論說(shuō) 出一點(diǎn) retrofit 相關(guān)內(nèi)容,我想了想,出是會(huì)出,但在此之前我想先出大概3~4篇文章用于介紹泛型、反射、注解和HTTP的相關(guān)內(nèi)容,當(dāng)你確實(shí)掌握之后,我打包票你只需要看一遍Retrofit官方教程的代碼示例,都不用看其它英文說(shuō)明,就可以輕松玩轉(zhuǎn)Retrofit。不服來(lái)戰(zhàn)!

文/怪盜kidou(簡(jiǎn)書(shū)作者)

原文鏈接:http://www.lxweimin.com/p/3108f1e44155

著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),并標(biāo)注“簡(jiǎn)書(shū)作者”。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容