Gson 使用总结 高级用法
生活随笔
收集整理的這篇文章主要介紹了
Gson 使用总结 高级用法
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Gson基本用法
參考:http://www.jianshu.com/p/e740196225a4Gson提供了fromJson() 和toJson() 兩個(gè)直接用于解析和生成的方法,前者實(shí)現(xiàn)反序列化,后者實(shí)現(xiàn)了序列化。//基本數(shù)據(jù)類型的解析 int i = gson.fromJson("100", int.class); //100 boolean b = gson.fromJson("true", boolean.class); // true String str = gson.fromJson("包青天", String.class); // 包青天//基本數(shù)據(jù)類型的生成 String jsonNumber = gson.toJson(100); // 100 String jsonBoolean = gson.toJson(true); // true String jsonString = gson.toJson("包青天"); // 包青天System.out.println(i + " " + b + " " + str + " " + jsonNumber + " " + jsonBoolean + " " + jsonString);//100 true 包青天 100 true "包青天"POJO類的生成與解析
class User {//省略構(gòu)造函數(shù)、toString方法等public String name;public int age; }//生成JSON User user = new User("包青天",24); String jsonObject = gson.toJson(user); // {"name":"包青天","age":24}//解析JSON: String jsonString = "{\"name\":\"包青天\",\"age\":24}"; User user = gson.fromJson(jsonString, User.class);
屬性重命名 @SerializedName
從上面POJO的生成與解析可以看出,JSON串中的 key 和JavaBean中的 field 是完全一致的,但有時(shí)候也會(huì)出現(xiàn)一些不和諧的情況,如:期望的json格式: {"name":"包青天","age":24,"emailAddress":"ikidou@example.com"} 實(shí)際的json格式: {"name":"包青天","age":24,"email_address":"ikidou@example.com"}這對(duì)于使用PHP作為后臺(tái)開發(fā)語(yǔ)言時(shí)很常見(jiàn)的情況,php和js在命名時(shí)一般采用下劃線風(fēng)格,而Java中一般采用的駝峰法,讓后臺(tái)的哥們改吧,前端和后臺(tái)都不爽,但要自己使用下劃線風(fēng)格時(shí)我會(huì)感到不適應(yīng),怎么辦?難到?jīng)]有兩全齊美的方法么?
我們知道Gson在序列化和反序列化時(shí)需要使用反射,說(shuō)到反射就不得不想到注解。一般各類庫(kù)都將注解放到annotations包下,打開源碼在com.google.gson包下果然有一個(gè)annotations,里面有一個(gè)SerializedName的注解類,這應(yīng)該就是我們要找的。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD }) public @interface SerializedName {String value();//方法名為value()的屬性不需要指定keyString[] alternate() default {};//默認(rèn)為空 }那么對(duì)于JSON中email_address這個(gè)屬性對(duì)應(yīng)POJO的屬性則變成:
@SerializedName("email_address") public String emailAddress;這樣的話,很好的保留了前端、后臺(tái)、Android/java各自的命名習(xí)慣。
為POJO字段提供備選屬性名
如果接口設(shè)計(jì)不嚴(yán)謹(jǐn)或者其它地方可以重用該類,其它字段都一樣,就emailAddress 字段不一樣,比如有下面三種情況那怎么辦?重新寫一個(gè)POJO類?{"name":"包青天","age":24,"emailAddress":"ikidou@example.com"} {"name":"包青天","age":24,"email_address":"ikidou@example.com"} {"name":"包青天","age":24,"email":"ikidou@example.com"}
SerializedName注解提供了兩個(gè)屬性,上面用到了其中一個(gè),另外還有一個(gè)屬性alternate,接收一個(gè)String數(shù)組。
@SerializedName(value = "emailAddress", alternate = {"email", "email_address"}) public String emailAddress;當(dāng)上面的三個(gè)屬性 email_address、email、emailAddress 中出現(xiàn)任意一個(gè)時(shí)均可以得到正確的結(jié)果。當(dāng)多種情況同時(shí)出時(shí),以最后一個(gè)出現(xiàn)的值為準(zhǔn)。
Gson中使用泛型?TypeToken
上面了解了JSON中的Number、boolean、Object和String,現(xiàn)在說(shuō)一下Array。當(dāng)我們要通過(guò)Gson解析這個(gè)jsonArray時(shí),一般有兩種方式:使用數(shù)組,使用List。而List對(duì)于增刪都是比較方便的,所以實(shí)際使用是還是List比較多。數(shù)組比較簡(jiǎn)單:String jsonArray = "[\"Android\",\"Java\",\"PHP\"]"; String[] strings = gson.fromJson(jsonArray, String[].class);但對(duì)于List將上面的代碼中的 String[].class 直接改為 List<String>.class 是行不通的。對(duì)于Java來(lái)說(shuō) List<String> 和 List<User> 這倆個(gè)的字節(jié)碼文件只一個(gè)那就是 List.class,這是Java泛型使用時(shí)要注意的問(wèn)題:泛型擦除。
為了解決的上面的問(wèn)題,Gson為我們提供了 TypeToken 來(lái)實(shí)現(xiàn)對(duì)泛型的支持,所以當(dāng)我們希望使用將以上的數(shù)據(jù)解析為 List<String> 時(shí)需要這樣寫:List<String> list = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());注:TypeToken的構(gòu)造方法是 protected 修飾的,所以上面才會(huì)寫成【new TypeToken<List<String>>() {}.getType()】而不是【new TypeToken<List<String>>().getType()】
泛型解析對(duì)POJO設(shè)計(jì)的影響
泛型的引入可以減少無(wú)關(guān)的代碼,如我現(xiàn)在所在公司接口返回的數(shù)據(jù)分為兩類:{"code":"0","message":"success","data":{}} {"code":"0","message":"success","data":[]}我們真正需要的是data所包含的數(shù)據(jù),而code只使用一次,message則幾乎不用。如果Gson不支持泛型或不知道Gson支持泛型的同學(xué)一定會(huì)這么定義POJO。
public class UserResponse {public int code;public String message;public User data; }當(dāng)其它接口的時(shí)候又重新定義一個(gè)XXResponse將data的類型改成XX。很明顯code、message被重復(fù)定義了多次,通過(guò)泛型的話我們可以將code和message字段抽取到一個(gè)Result的類中,這樣我們只需要編寫data字段所對(duì)應(yīng)的POJO即可,更專注于我們的業(yè)務(wù)邏輯。如:public class Result<T> {public int code;public String message;public T data; }那么對(duì)于data字段是User時(shí)則可以寫為 Result<User>,當(dāng)是個(gè)列表的時(shí)候?yàn)?Result<List<User>>,其它同理。
PS:嫌每次 new TypeToken<Result<XXX> 和 new TypeToken<Result<List<XXX>> 太麻煩,想進(jìn)一步封裝? 查看另一篇博客: 搞定Gson泛型封裝
Gson的流式反序列化?JsonReader
Gson提供了十幾個(gè)fromJson()和toJson()方法,前者實(shí)現(xiàn)反序列化,后者實(shí)現(xiàn)了序列化,常用的有如下5個(gè):public String toJson(Object src);//序列化,通用 //反序列化,自動(dòng)方式 public <T> T fromJson(String json, Class<T> classOfT);//普通的對(duì)象 public <T> T fromJson(String json, Type typeOfT);//使用泛型時(shí)使用 //反序列化,手動(dòng)方式 public <T> T fromJson(Reader json, Class<T> classOfT); public <T> T fromJson(Reader json, Type typeOfT);手動(dòng)的方式反序列化就是,使用stream包下的JsonReader類來(lái)手動(dòng)實(shí)現(xiàn)反序列化,和Android中使用pull解析XML是比較類似的。
String json = "{\"name\":\"包青天\",\"age\":\"24\"}"; User user = new User(); JsonReader reader = new JsonReader(new StringReader(json)); reader.beginObject(); // throws IOException while (reader.hasNext()) {String s = reader.nextName();switch (s) {case "name":user.name = reader.nextString();break;case "age":user.age = reader.nextInt(); //自動(dòng)轉(zhuǎn)換break;} } reader.endObject(); // throws IOException System.out.println(user.name+" "+user.age); // 包青天 24其實(shí)自動(dòng)方式最終都是通過(guò)JsonReader來(lái)實(shí)現(xiàn)的,如果第一個(gè)參數(shù)是String類型,那么Gson會(huì)創(chuàng)建一個(gè)StringReader轉(zhuǎn)換成流操作。
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {if (json == null) return null;StringReader reader = new StringReader(json);T target = fromJson(reader, typeOfT);return target; }
Gson的流式序列化?JsonWriter
Gson.toJson方法列表可以看出用紅框選中的部分就是我們要找的東西。
提示:PrintStream(System.out) 、StringBuilder、StringBuffer和**Writer都實(shí)現(xiàn)了Appendable接口。User user = new User("包青天",24); gson.toJson(user,System.out); // 自動(dòng)寫到控制臺(tái)手動(dòng)方式寫到控制臺(tái)
JsonWriter writer = new JsonWriter(new OutputStreamWriter(System.out)); writer.beginObject() // throws IOException.name("name").value("包青天")//.name("age").value(24)//.name("email").nullValue() //演示null.endObject(); // throws IOException writer.close(); // throws IOException。{"name":"包青天","age":24,"email":null}除了beginObject、endObject外,還有beginArray和endArray,兩者可以相互嵌套,注意配對(duì)即可。JsonWriter writer = new JsonWriter(new OutputStreamWriter(System.out)); writer.beginObject() // throws IOException.name("name").value("包青天")//.name("興趣").beginArray().value("籃球").value("排球").endArray()//.endObject(); // throws IOException writer.close(); // throws IOException。{"name":"包青天","興趣":["籃球","排球"]}beginArray后不可以調(diào)用name方法,同樣beginObject后在調(diào)用value之前必須要調(diào)用name方法。
setIndent方法可以設(shè)置縮進(jìn)格式:
JsonWriter writer = new JsonWriter(new OutputStreamWriter(System.out)); writer.setIndent(" ");//設(shè)置縮進(jìn)格式,這種格式是Gson默認(rèn)的、顯示效果良好的格式 writer.beginObject().name("name").value("包青天").name("興趣").beginArray().value("籃球").value("足球").beginObject().name("數(shù)組里的元素不要求是同類型的").value(true).endObject().endArray().endObject(); writer.close();輸出{"name": "包青天","興趣": ["籃球","足球",{"數(shù)組里的元素不要求是統(tǒng)一類型的": true}] }
使用GsonBuilder配置Gson
一般情況下Gson類提供的 API已經(jīng)能滿足大部分的使用場(chǎng)景,但當(dāng)我們需要更多更特殊、更強(qiáng)大的功能時(shí),可以使用GsonBuilder配置Gson。例如,Gson在默認(rèn)情況下是不導(dǎo)出值為null的鍵的,當(dāng)我們需要導(dǎo)出完整的json串時(shí),或API接口要求沒(méi)有值必須用Null時(shí),可以這么配置:Gson gson = new GsonBuilder().serializeNulls()//序列化null.setDateFormat("yyyy-MM-dd") // 設(shè)置日期時(shí)間格式,另有2個(gè)重載方法。在序列化和反序化時(shí)均生效.setPrettyPrinting()//格式化輸出。設(shè)置后,gson序列號(hào)后的字符串為一個(gè)格式化的字符串.create(); User user = new User("包青天", new Date()); System.out.println(gson.toJson(user));輸出內(nèi)容為:{"name": "包青天","email": null,"date": "2017-09-12", }
字段過(guò)濾的四種方法
字段過(guò)濾是Gson中比較常用的技巧,特別是在Android中,在處理業(yè)務(wù)邏輯時(shí)可能需要在設(shè)置的POJO中加入一些字段,但顯然在序列化的過(guò)程中是不需要的,并且如果序列化還可能帶來(lái)一個(gè)問(wèn)題就是:循環(huán)引用 。那么在用Gson序列化之前為不防止這樣的事件情發(fā)生,你不得不作另外的處理。以一個(gè)商品分類Category 為例:
{"id": 1,"name": "電腦","children": [{"id": 100,"name": "筆記本"},{"id": 101,"name": "臺(tái)式機(jī)"}] }一個(gè)大分類,可以有很多小分類,那么顯然我們?cè)谠O(shè)計(jì)Category類時(shí)Category本身既可以是大分類,也可以是小分類。
并且為了處理業(yè)務(wù),我們還需要在子分類中保存父分類,最終會(huì)變成下面的情況:
public class Category {public int id;public String name;public List<Category> children;public Category parent; //因業(yè)務(wù)需要增加,但并不需要序列化 }但是上面的parent字段是因業(yè)務(wù)需要增加的,那么在序列化時(shí)并不需要,所以在序列化時(shí)就必須將其排除,那么在Gson中如何排除符合條件的字段呢?下面提供4種方法,大家可根據(jù)需要自行選擇合適的方式。
基于注解@Expose
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.FIELD})
public @interface Expose{boolean serialize() default true;//默認(rèn)序列化生效boolean deserialize() default true;//默認(rèn)反序列化生效
}@Expose 注解從名字上就可以看出是暴露的意思,所以該注解是用于對(duì)外暴露字段的。該注解必須和GsonBuilder配合使用:Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();//不配置時(shí)注解無(wú)效使用規(guī)則:簡(jiǎn)單說(shuō)來(lái)就是需要導(dǎo)出的字段上加上@Expose 注解,不導(dǎo)出的字段不加。注意是不導(dǎo)出的不加。由于兩個(gè)屬性都有默認(rèn)的值true,所有值為true的屬性都是可以不寫的。如果兩者都為true,只寫?@Expose 就可以。
拿上面的例子來(lái)說(shuō)就是:
public class Category {@Expose public int id;// 等價(jià)于 @Expose(deserialize = true, serialize = true)@Expose public String name;@Expose public List<Category> children;public Category parent; //不需要序列化,等價(jià)于 @Expose(deserialize = false, serialize = false) }
基于版本和注解@Since @Until
Gson在對(duì)基于版本的字段導(dǎo)出提供了兩個(gè)注解 @Since 和 @Until,需要和GsonBuilder.setVersion(Double)配合使用Since和Until注解的定義:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public @interface Since{double value(); }@Documented @Retention(RetentionPolicy.RUNTIME) @Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public @interface Until{double value(); }使用規(guī)則:當(dāng)GsonBuilder中設(shè)置的版本大于等于Since的值時(shí)該字段導(dǎo)出,小于Until的值時(shí)該該字段導(dǎo)出;當(dāng)一個(gè)字段被@Since和@Until同時(shí)注解時(shí),需兩者同時(shí)滿足條件。
class SinceUntilSample {@Since(4) public String since;//大于等于Since@Until(5) public String until;//小于Until@Since(4) @Until(5) public String all;//大于等于Since且小于Until }Gson gson = new GsonBuilder().setVersion(version).create(); System.out.println(gson.toJson(sinceUntilSample));//當(dāng)version <4時(shí),結(jié)果:{"until":"until"} //當(dāng)version >=4 && version <5時(shí),結(jié)果:{"since":"since","until":"until","all":"all"} //當(dāng)version >=5時(shí),結(jié)果:{"since":"since"}
基于訪問(wèn)修飾符
什么是修飾符?不知道的話建議看一下java.lang.reflect.Modifier類,這是一個(gè)工具類,里面為所有修飾符定義了相應(yīng)的靜態(tài)字段,并提供了很多靜態(tài)工具方法。System.out.println(Modifier.toString(Modifier.fieldModifiers()));//public protected private static final transient volatile使用方式:使用GsonBuilder.excludeFieldsWithModifiers構(gòu)建gson,支持int型的可變參數(shù),參數(shù)值由java.lang.reflect.Modifier提供。Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)//排除了具有private、final或stati修飾符的字段.create();基于策略(自定義規(guī)則)
上面介紹的了3種排除字段的方法,說(shuō)實(shí)話我除了@Expose以外,其它的都是只在Demo用上過(guò),用得最多的就是馬上要介紹的自定義規(guī)則啦,好處是功能強(qiáng)大、靈活,缺點(diǎn)是相比其它3種方法稍麻煩一點(diǎn),但也僅僅只是相對(duì)其它3種稍麻煩一點(diǎn)點(diǎn)而已。基于策略是利用Gson提供的ExclusionStrategy接口,同樣需要使用GsonBuilder,相關(guān)API 2個(gè),分別是addSerializationExclusionStrategy 和addDeserializationExclusionStrategy,分別針對(duì)序列化和反序化時(shí)。這里以序列化為例:Gson gson = new GsonBuilder().addSerializationExclusionStrategy(new ExclusionStrategy() {@Overridepublic boolean shouldSkipField(FieldAttributes f) {//返回值決定要不要排除該字段,return true為排除if ("finalField".equals(f.getName())) return true; //根據(jù)字段名排除Expose expose = f.getAnnotation(Expose.class); //獲取Expose注解if (expose != null && expose.deserialize() == false) return true; //根據(jù)Expose注解排除return false;}@Overridepublic boolean shouldSkipClass(Class<?> clazz) {//直接排除某個(gè)類 ,return true為排除return (clazz == int.class || clazz == Integer.class);}}).create();有沒(méi)有很強(qiáng)大?
自定義POJO與JSON的字段映射規(guī)則
GsonBuilder提供了setFieldNamingPolicy和setFieldNamingStrategy兩個(gè)方法,用來(lái)設(shè)置字段序列和反序列時(shí)字段映射的規(guī)則。1、GsonBuilder.setFieldNamingPolicy方法與 Gson 提供的另一個(gè)枚舉類FieldNamingPolicy配合使用:class User {String emailAddress; }Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create();//默認(rèn) User user = new User("baiqiantao@sina.com"); System.out.println(gson.toJson(user));該枚舉類提供的五種實(shí)現(xiàn)方式的效果分別為:
| FieldNamingPolicy | 結(jié)果 |
| IDENTITY 個(gè)性/特性/恒等式 | {"emailAddress":"baiqiantao@sina.com"} |
| LOWER_CASE_WITH_DASHES 小寫+破折號(hào) | {"email-address":"baiqiantao@sina.com"} |
| LOWER_CASE_WITH_UNDERSCORES?小寫+下劃線 | {"email_address":"baiqiantao@sina.com"} |
| UPPER_CAMEL_CASE 駝峰式+首字母大寫 | {"EmailAddress":"baiqiantao@sina.com"} |
| UPPER_CAMEL_CASE_WITH_SPACES?駝峰式+空格 | {"Email Address":"baiqiantao@sina.com"} |
2、GsonBuilder.setFieldNamingStrategy方法需要與Gson提供的FieldNamingStrategy接口配合使用,用于實(shí)現(xiàn)將POJO的字段與JSON的字段相對(duì)應(yīng)。上面的FieldNamingPolicy實(shí)際上也實(shí)現(xiàn)了FieldNamingStrategy接口,也就是說(shuō)FieldNamingPolicy也可以使用setFieldNamingStrategy方法。public enum FieldNamingPolicy implements FieldNamingStrategy用法:
Gson gson = new GsonBuilder().setFieldNamingStrategy(new FieldNamingStrategy() {@Overridepublic String translateName(Field f) {//實(shí)現(xiàn)自己的規(guī)則return null;} }) .create();注意: @SerializedName注解擁有最高優(yōu)先級(jí),在加有@SerializedName注解的字段上FieldNamingStrategy不生效!
TypeAdapter 自定義(反)序列化
TypeAdapter 是Gson自2.0(源碼注釋上說(shuō)的是2.1)開始版本提供的一個(gè)抽象類,用于接管某種類型的序列化和反序列化過(guò)程,包含兩個(gè)主要方法 write(JsonWriter,T) 和 read(JsonReader),其它的方法都是final方法并最終調(diào)用這兩個(gè)抽象方法。public abstract class TypeAdapter<T> {public abstract void write(JsonWriter out, T value) throws IOException;public abstract T read(JsonReader in) throws IOException;//其它final方法就不貼出來(lái)了,包括toJson、toJsonTree、fromJson、fromJsonTree和nullSafe等方法。 }注意:TypeAdapter 以及 JsonSerializer 和 JsonDeserializer 都需要與 .registerTypeAdapter 或 .registerTypeHierarchyAdapter 配合使用,下面將不再重復(fù)說(shuō)明。
TypeAdapter 使用示例1
User user = new User("包青天", 24, "baiqiantao@sina.com";
Gson gson = new GsonBuilder().registerTypeAdapter(User.class, new UserTypeAdapter())//為User注冊(cè)TypeAdapter.create();
System.out.println(gson.toJson(user));UserTypeAdapter的定義:public class UserTypeAdapter extends TypeAdapter<User> {@Overridepublic void write(JsonWriter out, User value) throws IOException {out.beginObject();out.name("name").value(value.name);out.name("age").value(value.age);out.name("email").value(value.email);out.endObject();}@Overridepublic User read(JsonReader in) throws IOException {User user = new User();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();return user;} }當(dāng)我們?yōu)?User.class 注冊(cè)了 TypeAdapter 之后,那些之前介紹的@SerializedName、FieldNamingStrategy、Since、Until、Expos通通都黯然失色,失去了效果,只會(huì)調(diào)用我們實(shí)現(xiàn)的 UserTypeAdapter.write(JsonWriter, User) 方法,我想怎么寫就怎么寫。
TypeAdapter?使用示例2
再說(shuō)一個(gè)場(chǎng)景,之前已經(jīng)說(shuō)過(guò)Gson有一定的容錯(cuò)機(jī)制,比如將字符串 "24" 轉(zhuǎn)成整數(shù)24,但如果有些情況下給你返了個(gè)空字符串怎么辦?雖然這是服務(wù)器端的問(wèn)題,但這里我們只是做一個(gè)示范,不改服務(wù)端的邏輯我們?cè)趺慈蒎e(cuò)。根據(jù)我們上面介紹的,我只需注冊(cè)一個(gè) TypeAdapter 把?Integer/int 的序列化和反序列化過(guò)程接管就行了:Gson gson = new GsonBuilder().registerTypeAdapter(Integer.class, new TypeAdapter<Integer>() {//接管【Integer】類型的序列化和反序列化過(guò)程//注意,這里只是接管了Integer類型,并沒(méi)有接管int類型,要接管int類型需要添加【int.class】@Overridepublic void write(JsonWriter out, Integer value) throws IOException {out.value(String.valueOf(value)); }@Overridepublic Integer read(JsonReader in) throws IOException {try {return Integer.parseInt(in.nextString());} catch (NumberFormatException e) {return -1;//當(dāng)時(shí)Integer時(shí),解析失敗時(shí)返回-1}}}).registerTypeAdapter(int.class, new TypeAdapter<Integer>() {//接管【int】類型的序列化和反序列化過(guò)程//泛型只能是引用類型,而不能是基本類型@Overridepublic void write(JsonWriter out, Integer value) throws IOException {out.value(String.valueOf(value)); }@Overridepublic Integer read(JsonReader in) throws IOException {try {return Integer.parseInt(in.nextString());} catch (NumberFormatException e) {return -2;//當(dāng)時(shí)int時(shí),解析失敗時(shí)返回-2}}}).create();int i = gson.fromJson("包青天", Integer.class); //-1 int i2 = gson.fromJson("包青天", int.class); //-2 System.out.println(i + " " + i2);//-1 -2Json(De)Serializer
JsonSerializer 和JsonDeserializer 不用像TypeAdapter一樣,必須要實(shí)現(xiàn)序列化和反序列化的過(guò)程,你可以據(jù)需要選擇,如只接管序列化的過(guò)程就用 JsonSerializer ,只接管反序列化的過(guò)程就用 JsonDeserializer ,如上面的需求可以用下面的代碼。Gson gson = new GsonBuilder().registerTypeAdapter(Integer.class, new JsonDeserializer<Integer>() {@Overridepublic Integer deserialize(JsonElement j, Type t, JsonDeserializationContext c) throws JsonParseException {try {return j.getAsInt();} catch (NumberFormatException e) {return -1;}}}).create();
下面是所有數(shù)字(Number的子類)都轉(zhuǎn)成序列化為字符串的例子:
JsonSerializer<Number> numberJsonSerializer = new JsonSerializer<Number>() {@Overridepublic JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {return new JsonPrimitive(String.valueOf(src));} }; Gson gson = new GsonBuilder().registerTypeAdapter(Integer.class, numberJsonSerializer).registerTypeAdapter(Long.class, numberJsonSerializer).registerTypeAdapter(Float.class, numberJsonSerializer).registerTypeAdapter(Double.class, numberJsonSerializer).create();
泛型與繼承
使用 registerTypeAdapter 時(shí)不能使用父類來(lái)替上面的子類型,這也是為什么要分別注冊(cè)而不直接使用Number.class的原因。不過(guò)換成 registerTypeHierarchyAdapter 就可以使用 Number.class 而不用一個(gè)一個(gè)的單獨(dú)注冊(cè)子類啦!
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter (Number.class, numberJsonSerializer).create();這種方式在List上體現(xiàn)更為明顯,當(dāng)我們使用registerTypeAdapter為L(zhǎng)ist.class注冊(cè)TypeAdapter時(shí),其對(duì)List的子類(如ArrayList.class)并無(wú)效,所以我們必須使用registerTypeHierarchyAdapter方法注冊(cè)。
兩者的區(qū)別:
| 支持泛型 | 是 | 否 |
| 支持繼承 | 否 | 是 |
注意:如果一個(gè)被序列化的對(duì)象本身就帶有泛型,且注冊(cè)了相應(yīng)的TypeAdapter,那么必須調(diào)用Gson.toJson(Object,Type),明確告訴Gson對(duì)象的類型;否則,將跳過(guò)此注冊(cè)的TypeAdapter。
Type type = new TypeToken<List<User>>() {}.getType();//被序列化的對(duì)象帶有【泛型】 TypeAdapter<List<User>> typeAdapter = new TypeAdapter<List<User>>() { .../*省略實(shí)現(xiàn)的方法*/ }; Gson gson = new GsonBuilder().registerTypeAdapter(type, typeAdapter)//注冊(cè)了與此type相應(yīng)的TypeAdapter.create(); String result = gson.toJson(list, type);//明確指定type時(shí)才會(huì)使用注冊(cè)的TypeAdapter托管序列化和反序列化 String result2 = gson.toJson(list);//不指定type時(shí)使用系統(tǒng)默認(rèn)的機(jī)制進(jìn)行序列化和反序列化
TypeAdapterFactory
TypeAdapterFactory,見(jiàn)名知意,用于創(chuàng)建 TypeAdapter 的工廠類。使用方式:與GsonBuilder.registerTypeAdapterFactory配合使用,通過(guò)對(duì)比Type,確定有沒(méi)有對(duì)應(yīng)的TypeAdapter,沒(méi)有就返回null,有則返回(并使用)自定義的TypeAdapter。Gson gson = new GsonBuilder().registerTypeAdapterFactory(new TypeAdapterFactory() {@Overridepublic <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {if (type.getType() == Integer.class || type.getType() == int.class) return intTypeAdapter;return null;}}).create();
注解?@JsonAdapter
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD})//作用在類或字段上
public @interface JsonAdapter{Class<?> value();boolean nullSafe() default true;
}上面說(shuō)JsonSerializer和JsonDeserializer都要配合 GsonBuilder.registerTypeAdapter 使用,但每次使用都要注冊(cè)也太麻煩了,JsonAdapter注解就是為了解決這個(gè)痛點(diǎn)的。使用方法:@JsonAdapter(UserTypeAdapter.class) //加在類上
public class User {public String name;public int age;
}使用時(shí)就不需要再使用 GsonBuilder去注冊(cè) UserTypeAdapter 了。注意:JsonAdapter的優(yōu)先級(jí)比 GsonBuilder.registerTypeAdapter 的優(yōu)先級(jí)還高。2017-9-12
總結(jié)
以上是生活随笔為你收集整理的Gson 使用总结 高级用法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。