Gson使用指南4.docx
《Gson使用指南4.docx》由会员分享,可在线阅读,更多相关《Gson使用指南4.docx(13页珍藏版)》请在冰豆网上搜索。
Gson使用指南4
注:
此系列基于Gson2.4。
本次文章的主要内容:
∙TypeAdapter
∙JsonSerializer与JsonDeserializer
∙TypeAdapterFactory
∙@JsonAdapter注解
∙TypeAdapter与JsonSerializer、JsonDeserializer对比
∙TypeAdapter实例
∙结语
∙后期预告
一、TypeAdapter
TypeAdapter 是Gson自2.0(源码注释上说的是2.1)开始版本提供的一个抽象类,用于接管某种类型的序列化和反序列化过程,包含两个注要方法 write(JsonWriter,T) 和 read(JsonReader) 其它的方法都是final方法并最终调用这两个抽象方法。
1
2
3
4
5
publicabstractclassTypeAdapter{
publicabstractvoidwrite(JsonWriterout,Tvalue)throwsIOException;
publicabstractTread(JsonReaderin)throwsIOException;
//其它final方法就不贴出来了,包括`toJson`、`toJsonTree`、`toJson`和`nullSafe`方法。
}
注意:
TypeAdapter以及JsonSerializer和JsonDeserializer都需要与 GsonBuilder.registerTypeAdapter 示或GsonBuilder.registerTypeHierarchyAdapter配合使用,下面将不再重复说明。
使用示例:
1
2
3
4
5
6
7
Useruser=newUser("怪盗kidou",24);
user.emailAddress="ikidou@";
Gsongson=newGsonBuilder()
//为User注册TypeAdapter
.registerTypeAdapter(User.class,newUserTypeAdapter())
.create();
System.out.println(gson.toJson(user));
UserTypeAdapter的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
publicclassUserTypeAdapterextendsTypeAdapter{
@Override
publicvoidwrite(JsonWriterout,Uservalue)throwsIOException{
out.beginObject();
out.name("name").value(value.name);
out.name("age").value(value.age);
out.name("email").value(value.email);
out.endObject();
}
@Override
publicUserread(JsonReaderin)throwsIOException{
Useruser=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;
}
}
当我们为User.class 注册了 TypeAdapter之后,只要是操作User.class 那些之前介绍的@SerializedName 、FieldNamingStrategy、Since、Until、Expos通通都黯然失色,失去了效果,只会调用我们实现的UserTypeAdapter.write(JsonWriter,User) 方法,我想怎么写就怎么写。
再说一个场景,在该系列的第一篇文章就说到了Gson有一定的容错机制,比如将字符串 "24" 转成int的24,但如果有些情况下给你返了个空字符串怎么办(有人给我评论问到这个问题)?
虽然这是服务器端的问题,但这里我们只是做一个示范。
int型会出错是吧,根据我们上面介绍的,我注册一个TypeAdapter把序列化和反序列化的过程接管不就行了?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Gsongson=newGsonBuilder()
.registerTypeAdapter(Integer.class,newTypeAdapter(){
@Override
publicvoidwrite(JsonWriterout,Integervalue)throwsIOException{
out.value(String.valueOf(value));
}
@Override
publicIntegerread(JsonReaderin)throwsIOException{
try{
returnInteger.parseInt(in.nextString());
}catch(NumberFormatExceptione){
return-1;
}
}
})
.create();
System.out.println(gson.toJson(100));//结果:
"100"
System.out.println(gson.fromJson("\"\"",Integer.class));//结果:
-1
注:
测试空串的时候一定是"\"\""而不是"",""代表的是没有json串,"\"\""才代表json里的""。
你说这一接管就要管两样好麻烦呀,我明明只想管序列化(或反列化)的过程的,另一个过程我并不关心,难道没有其它更简单的方法么?
当然有!
就是接下来要介绍的JsonSerializer与JsonDeserializer。
二、JsonSerializer与JsonDeserializer
JsonSerializer 和JsonDeserializer 不用像TypeAdapter一样,必须要实现序列化和反序列化的过程,你可以据需要选择,如只接管序列化的过程就用 JsonSerializer ,只接管反序列化的过程就用 JsonDeserializer ,如上面的需求可以用下面的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Gsongson=newGsonBuilder()
.registerTypeAdapter(Integer.class,newJsonDeserializer(){
@Override
publicIntegerdeserialize(JsonElementjson,TypetypeOfT,JsonDeserializationContextcontext)throwsJsonParseException{
try{
returnjson.getAsInt();
}catch(NumberFormatExceptione){
return-1;
}
}
})
.create();
System.out.println(gson.toJson(100));//结果:
100
System.out.println(gson.fromJson("\"\"",Integer.class));//结果-1
下面是所有数字都转成序列化为字符串的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
JsonSerializernumberJsonSerializer=newJsonSerializer(){
@Override
publicJsonElementserialize(Numbersrc,TypetypeOfSrc,JsonSerializationContextcontext){
returnnewJsonPrimitive(String.valueOf(src));
}
};
Gsongson=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));//结果:
"100.0"
注:
registerTypeAdapter必须使用包装类型,所以int.class,long.class,float.class和double.class是行不通的。
同时不能使用父类来替上面的子类型,这也是为什么要分别注册而不直接使用Number.class的原因。
上面特别说明了registerTypeAdapter不行,那就是有其它方法可行咯?
当然!
换成registerTypeHierarchyAdapter 就可以使用Number.class而不用一个一个的当独注册啦!
registerTypeAdapter与registerTypeHierarchyAdapter的区别:
registerTypeAdapter
registerTypeHierarchyAdapter
支持泛型
是
否
支持继承
否
是
注:
如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。
1
2
3
4
5
6
7
8
9
10
11
12
Typetype=newTypeToken>(){}.getType();
TypeAdaptertypeAdapter=newTypeAdapter>(){
//略
};
Gsongson=newGsonBuilder()
.registerTypeAdapter(type,typeAdapter)
.create();
Listlist=newArrayList<>();
list.add(newUser("a",11));
list.add(newUser("b",22));
//注意,多了个type参数
Stringresult=gson.toJson(list,type);
三、TypeAdapterFactory
TypeAdapterFactory,见名知意,用于创建TypeAdapter的工厂类,通过对比Type,确定有没有对应的TypeAdapter,没有就返回null,与GsonBuilder.registerTypeAdapterFactory配合使用。
1
2
3
4
5
6
7
8
Gsongson=newGsonBuilder()
.registerTypeAdapterFactory(newTypeAdapterFactory(){
@Override
publicTypeAdaptercreate(Gsongson,TypeTokentype){
returnnull;
}
})
.create();
四、@JsonAdapter注解
JsonAdapter相较之前介绍的SerializedName、FieldNamingStrategy、Since、Until、Expos这几个注解都是比较特殊的,其它的几个都是用在POJO的字段上,而这一个是用在POJO类上的,接收一个参数,且必须是TypeAdpater,JsonSerializer或JsonDeserializer这三个其中之一。
上面说JsonSerializer和JsonDeserializer都要配合GsonBuilder.registerTypeAdapter使用,但每次使用都要注册也太麻烦了,JsonAdapter就是为了解决这个痛点的。
使用方法(以User为例):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@JsonAdapter(UserTypeAdapter.class)//加在类上
publicclassUser{
publicUser(){
}
publicUser(Stringname,intage){
this.name=name;
this.age=age;
}
publicUser(Stringname,intage,Stringemail){
this.name=name;
this.age=age;
this.email=email;
}
publicStringname;
publicintage;
@SerializedName(value="emailAddress")
publicStringemail;
}
使用时不用再使用 GsonBuilder去注册UserTypeAdapter了。
注:
@JsonAdapter 仅支持 TypeAdapter或TypeAdapterFactory
1
2
3
4
5
Gsongson=newGson();
Useruser=newUser("怪盗kidou",24,"ikidou@");
System.out.println(gson.toJson(user));
//结果:
{"name":
"怪盗kidou","age":
24,"email":
"ikidou@"}
//为区别结果,特意把email字段与@SerializedName注解中设置的不一样
注意:
JsonAdapter的优先级比GsonBuilder.registerTypeAdapter的优先级更高。
五、TypeAdapter与JsonSerializer、JsonDeserializer对比
TypeAdapter
JsonSerializer、JsonDeserializer
引入版本
2.0
1.x
StreamAPI
支持
不支持*,需要提前生成JsonElement
内存占用
小
比TypeAdapter大
效率
高
比TypeAdapter低
作用范围
序列化 和 反序列化
序列化 或 反序列化
六、TypeAdapter实例
注:
这里的TypeAdapter泛指TypeAdapter、JsonSerializer和JsonDeserializer。
这里的TypeAdapter上面讲了一个自动将字符串形式的数值转换成int型时可能出现空字符串的问题,下面介绍一个其它读者的需求:
服务器返回的数据中data字段类型不固定,比如请求成功data是一个List,不成功的时候是String类型,这样前端在使用泛型解析的时候,怎么去处理呢?
其实这个问题的原因主要由服务器端造成的,接口设计时没有没有保证数据的一致性,正确的数据返回姿势:
同一个接口任何情况下不得改变返回类型,要么就不要返,要么就返空值,如null、[],{}。
但这里还是给出解决方案:
方案一:
1
2
3
4
5
6
7
8
9
10
11
12
13
Gsongson=newGsonBuilder().registerTypeHierarchyAdapter(List.class,newJsonDeserializer>>(){
@Override
publicList
>deserialize(JsonElementjson,TypetypeOfT,JsonDeserializationContextcontext)throwsJsonParseException{
if(json.isJsonArray()){
//这里要自己负责解析了
GsonnewGson=newGson();
returnnewGson.fromJson(json,typeOfT);
}else{
//和接口类型不符,返回空List
returnCollections.EMPTY_LIST;
}
}
}).create();
方案二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Gsongson=newGsonBuilder().registerTypeHierarchyAdapter(List.class,newJsonDeserializer>>(){
@Override
publicList
>deserialize(JsonElementjson,TypetypeOfT,JsonDeserializationContextcontext)throwsJsonParseException{
if(json.isJsonArray()){
JsonArrayarray=json.getAsJsonArray();
TypeitemType=((ParameterizedType)typeOfT).getActualTypeArguments()[0];
Listlist=newArrayList<>();
for(inti=0;i JsonElementelement=array.get(i);
Objectitem=context.deserialize(element,itemType);
list.add(item);
}
returnlist;
}else{
//和接口类型不符,返回空List
returnCollections.EMPTY_LIST;
}
}
}).create();
要注意的点:
∙必须使用registerTypeHierarchyAdapter方法,不然对List的子类无效,但如果POJO中都是使用List,那么可以使用registerTypeAdapter。
∙对于是数组的情况,需要创建一个新的Gson,不可以直接使用context,不然gson又会调我们自定义的JsonDeserializer造成递归调用,方案二没有重新创建Gson,那么就需要提取出List中E的类型,然后分别反序列化