本文為作者根據(jù)日常使用結(jié)合Gson源碼注釋及wiki所作的原創(chuàng)內(nèi)容,轉(zhuǎn)載請注明出處捂齐。
該系列其它文章
注:此系列基于Gson 2.4布疙。
本次的主要內(nèi)容:
- 字段過濾的幾種方法
- 基于
@Expose
注解 - 基于版本
- 基于訪問修飾符
- 基于策略(作者最常用)
- 基于
- POJO與JSON的字段映射規(guī)則
一、字段過濾的幾種方法
字段過濾Gson中比較常用的技巧,特別是在Android中凿滤,在處理業(yè)務邏輯時可能需要在設置的POJO中加入一些字段挚冤,但顯然在序列化的過程中是不需要的况增,并且如果序列化還可能帶來一個問題就是 循環(huán)引用 ,那么在用Gson序列化之前為不防止這樣的事件情發(fā)生训挡,你不得不作另外的處理澳骤。
以一個商品分類Category
為例。
{
"id": 1,
"name": "電腦",
"children": [
{
"id": 100,
"name": "筆記本"
},
{
"id": 101,
"name": "臺式機"
}
]
}
一個大分類澜薄,可以有很多小分類为肮,那么顯然我們在設計Category
類時Category
本身既可以是大分類,也可以是小分類肤京。
public class Category {
public int id;
public String name;
public List<Category> children;
}
但是為了處理業(yè)務颊艳,我們還需要在子分類中保存父分類,最終會變成下面的情況
public class Category {
public int id;
public String name;
public List<Category> children;
//因業(yè)務需要增加,但并不需要序列化
public Category parent;
}
但是上面的parent
字段是因業(yè)務需要增加的棋枕,那么在序列化是并不需要白修,所以在序列化時就必須將其排除,那么在Gson
中如何排除符合條件的字段呢?下面提供4種方法戒悠,大家可根據(jù)需要自行選擇合適的方式熬荆。
基于@Expose注解
@Expose提供了兩個屬性,且都有默認值绸狐,開發(fā)者可以根據(jù)需要設置不同的值卤恳。
@Expose 注解從名字上就可以看出是暴露的意思,所以該注解是用于對外暴露字段的寒矿⊥涣眨可是我們以前用Gson的時候也沒有@Expose 注解還不是正確的序列化為JSON了么?是的,所以該注解在使用
new Gson()
時是不會發(fā)生作用符相。畢竟最常用的API要最簡單拆融,所以該注解必須和GsonBuilder
配合使用。
使用方法: 簡單說來就是需要導出的字段上加上@Expose 注解啊终,不導出的字段不加镜豹。注意是不導出的不加。
@Expose //
@Expose(deserialize = true,serialize = true) //序列化和反序列化都都生效蓝牲,等價于上一條
@Expose(deserialize = true,serialize = false) //反序列化時生效
@Expose(deserialize = false,serialize = true) //序列化時生效
@Expose(deserialize = false,serialize = false) // 和不寫注解一樣
注:根據(jù)上面的圖片可以得出趟脂,所有值為true
的屬性都是可以不寫的(默認值是true)。
拿上面的例子來說就是
public class Category {
@Expose public int id;
@Expose public String name;
@Expose public List<Category> children;
//不需要序列化,所以不加 @Expose 注解例衍,
//等價于 @Expose(deserialize = false,serialize = false)
public Category parent;
}
在使用Gson時也不能只是簡單的new Gson()
了昔期。
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
gson.toJson(category);
基于版本
Gson在對基于版本的字段導出提供了兩個注解 @Since
和 @Until
,和GsonBuilder.setVersion(Double)
配合使用。@Since
和 @Until
都接收一個Double
值佛玄。
使用方法:當前版本(GsonBuilder中設置的版本) 大于等于Since的值時該字段導出硼一,小于Until的值時該該字段導出。
class SinceUntilSample {
@Since(4)
public String since;
@Until(5)
public String until;
}
public void sineUtilTest(double version){
SinceUntilSample sinceUntilSample = new SinceUntilSample();
sinceUntilSample.since = "since";
sinceUntilSample.until = "until";
Gson gson = new GsonBuilder().setVersion(version).create();
System.out.println(gson.toJson(sinceUntilSample));
}
//當version <4時梦抢,結(jié)果:{"until":"until"}
//當version >=4 && version <5時般贼,結(jié)果:{"since":"since","until":"until"}
//當version >=5時,結(jié)果:{"since":"since"}
注:當一個字段被同時注解時奥吩,需兩者同時滿足條件具伍。
基于訪問修飾符
什么是修飾符? public
、static
圈驼、final
、private
望几、protected
這些就是绩脆,所以這種方式也是比較特殊的。
使用方式:
class ModifierSample {
final String finalField = "final";
static String staticField = "static";
public String publicField = "public";
protected String protectedField = "protected";
String defaultField = "default";
private String privateField = "private";
}
使用GsonBuilder.excludeFieldsWithModifiers
構(gòu)建gson,支持int
形的可變參數(shù),值由java.lang.reflect.Modifier
提供靴迫,下面的程序排除了privateField
惕味、 finalField
和staticField
三個字段。
ModifierSample modifierSample = new ModifierSample();
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
.create();
System.out.println(gson.toJson(modifierSample));
// 結(jié)果:{"publicField":"public","protectedField":"protected","defaultField":"default"}
到此為止玉锌,Gson提供的所有注解就還有一個@JsonAdapter
沒有介紹了名挥,而@JsonAdapter
將和TypeAdapter
將作為該系列第4篇也是最后一篇文章的主要內(nèi)容。
基于策略(自定義規(guī)則)
上面介紹的了3種排除字段的方法主守,說實話我除了@Expose以外禀倔,其它的都是只在Demo用上過,用得最多的就是馬上要介紹的自定義規(guī)則参淫,好處是功能強大救湖、靈活,缺點是相比其它3種方法稍麻煩一點涎才,但也僅僅只是想對其它3種稍麻煩一點而已鞋既。
基于策略是利用Gson提供的ExclusionStrategy
接口,同樣需要使用GsonBuilder
,相關(guān)API 2個耍铜,分別是addSerializationExclusionStrategy
和addDeserializationExclusionStrategy
分別針對序列化和反序化時邑闺。這里以序列化為例。
例如:
Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
// 這里作判斷棕兼,決定要不要排除該字段,return true為排除
if ("finalField".equals(f.getName())) return true; //按字段名排除
Expose expose = f.getAnnotation(Expose.class);
if (expose != null && expose.deserialize() == false) return true; //按注解排除
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
// 直接排除某個類 陡舅,return true為排除
return (clazz == int.class || clazz == Integer.class);
}
})
.create();
有沒有很強大?
二、 POJO與JSON的字段映射規(guī)則
之前在你真的會用Gson嗎?Gson使用指南(二) 屬性重命名時 介紹了@SerializedName
這個注解的使用程储,本節(jié)的內(nèi)容與上一次差不多的蹭沛,但既然叫映射規(guī)則那么說的自然是有規(guī)律的情況。
還是之前User的例子章鲤,已經(jīng)去除所有注解:
User user = new User("怪盜kidou", 24);
user.emailAddress = "ikidou@example.com";
GsonBuilder
提供了FieldNamingStrategy
接口和setFieldNamingPolicy
和setFieldNamingStrategy
兩個方法摊灭。
默認實現(xiàn)
GsonBuilder.setFieldNamingPolicy
方法與Gson提供的另一個枚舉類FieldNamingPolicy
配合使用,該枚舉類提供了5種實現(xiàn)方式分別為:
FieldNamingPolicy | 結(jié)果(僅輸出emailAddress字段) |
---|---|
IDENTITY | {"emailAddress":"ikidou@example.com"} |
LOWER_CASE_WITH_DASHES | {"email-address":"ikidou@example.com"} |
LOWER_CASE_WITH_UNDERSCORES | {"email_address":"ikidou@example.com"} |
UPPER_CAMEL_CASE | {"EmailAddress":"ikidou@example.com"} |
UPPER_CAMEL_CASE_WITH_SPACES | {"Email Address":"ikidou@example.com"} |
自定義實現(xiàn)
GsonBuilder.setFieldNamingStrategy
方法需要與Gson提供的FieldNamingStrategy
接口配合使用败徊,用于實現(xiàn)將POJO的字段與JSON的字段相對應帚呼。上面的FieldNamingPolicy
實際上也實現(xiàn)了FieldNamingStrategy
接口,也就是說FieldNamingPolicy
也可以使用setFieldNamingStrategy
方法皱蹦。
用法:
Gson gson = new GsonBuilder()
.setFieldNamingStrategy(new FieldNamingStrategy() {
@Override
public String translateName(Field f) {
//實現(xiàn)自己的規(guī)則
return null;
}
})
.create();
注意: @SerializedName
注解擁有最高優(yōu)先級煤杀,在加有@SerializedName
注解的字段上FieldNamingStrategy
不生效!
本文完
下期預告(本系列最終篇):
- 無所不能的TypeAdapter
我最近剛剛開通了微信公眾號(怪盜kidou)沪哺,歡迎關(guān)注