专栏名称: ImportNew
伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
目录
相关文章推荐
芋道源码  ·  DeepSeek浪潮,Spring也赶上了?! ·  2 天前  
芋道源码  ·  SpringBoot ... ·  2 天前  
51好读  ›  专栏  ›  ImportNew

Gson 使用指南(4)

ImportNew  · 公众号  · Java  · 2017-03-21 20:59

正文

(点击 上方公众号 ,可快速关注)


来源:怪盗kidou,

www.jianshu.com/p/3108f1e44155

如有好文章投稿,请点击 → 这里了解详情


注:此系列基于Gson 2.4。


本次文章的主要内容:


  • TypeAdapter

  • JsonSerializer与JsonDeserializer

  • TypeAdapterFactory

  • @JsonAdapter注解

  • TypeAdapter与 JsonSerializer、JsonDeserializer对比

  • TypeAdapter实例

  • 结语

  • 后期预告


一、TypeAdapter


TypeAdapter 是Gson自2.0(源码注释上说的是2.1)开始版本提供的一个抽象类,用于接管某种类型的序列化和反序列化过程,包含两个注要方法 write(JsonWriter,T) 和 read(JsonReader) 其它的方法都是final方法并最终调用这两个抽象方法。


public abstract class TypeAdapter {

public abstract void write(JsonWriter out, T value) throws IOException;

public abstract T read(JsonReader in) throws IOException;

//其它final 方法就不贴出来了,包括`toJson`、`toJsonTree`、`toJson`和`nullSafe`方法。

}


注意:TypeAdapter 以及 JsonSerializer 和 JsonDeserializer 都需要与 GsonBuilder.registerTypeAdapter 示或GsonBuilder.registerTypeHierarchyAdapter配合使用,下面将不再重复说明。


使用示例:


User user = new User("怪盗kidou", 24);

user.emailAddress = "[email protected]";

Gson gson = new GsonBuilder()

//为User注册TypeAdapter

.registerTypeAdapter(User.class, new UserTypeAdapter())

.create();

System.out.println(gson.toJson(user));


UserTypeAdapter的定义:


public class UserTypeAdapter extends TypeAdapter {

@Override

public 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();

}

@Override

public 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;

}

}


当我们为User.class 注册了 TypeAdapter之后,只要是操作User.class 那些之前介绍的@SerializedName 、FieldNamingStrategy、Since、Until、Expos通通都黯然失色,失去了效果,只会调用我们实现的UserTypeAdapter.write(JsonWriter, User) 方法,我想怎么写就怎么写。


再说一个场景,在该系列的第一篇文章就说到了Gson有一定的容错机制,比如将字符串 "24" 转成int 的24,但如果有些情况下给你返了个空字符串怎么办(有人给我评论问到这个问题)?虽然这是服务器端的问题,但这里我们只是做一个示范。


int型会出错是吧,根据我们上面介绍的,我注册一个TypeAdapter 把 序列化和反序列化的过程接管不就行了?


Gson gson = new GsonBuilder()

.registerTypeAdapter(Integer.class, new TypeAdapter () {

@Override

public void write(JsonWriter out, Integer value) throws IOException {

out.value(String.valueOf(value));

}

@Override

public Integer read(JsonReader in) throws IOException {

try {

return Integer.parseInt(in.nextString());

} catch (NumberFormatException e) {

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 ,如上面的需求可以用下面的代码。


Gson gson = new GsonBuilder()

.registerTypeAdapter(Integer.class, new JsonDeserializer () {

@Override

public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

try {

return json.getAsInt();

} catch (NumberFormatException e) {

return -1;

}

}

})

.create();

System.out.println(gson.toJson(100)); //结果:100

System.out.println(gson.fromJson("\"\"", Integer.class)); //结果-1


下面是所有数字都转成序列化为字符串的例子


JsonSerializer numberJsonSerializer = new JsonSerializer () {

@Override

public 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();

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的区别:



注:如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。


Type type = new TypeToken >() {}.getType();

TypeAdapter typeAdapter = new TypeAdapter >() {

//略

};

Gson gson = new GsonBuilder()

.registerTypeAdapter(type, typeAdapter)

.create();

List list = new ArrayList<>();

list.add(new User("a",11));

list.add(new User("b",22));

//注意,多了个type参数

String result = gson.toJson(list, type);


三、TypeAdapterFactory


TypeAdapterFactory,见名知意,用于创建TypeAdapter的工厂类,通过对比Type,确定有没有对应的TypeAdapter,没有就返回null,与GsonBuilder.registerTypeAdapterFactory配合使用。


Gson gson = new GsonBuilder()

.registerTypeAdapterFactory(new TypeAdapterFactory() {

@Override

public TypeAdapter create(Gson gson, TypeToken type) {

return null;

}

})

.create();


四、@JsonAdapter注解


JsonAdapter相较之前介绍的SerializedName、FieldNamingStrategy、Since、Until、Expos这几个注解都是比较特殊的,其它的几个都是用在POJO的字段上,而这一个是用在POJO类上的,接收一个参数,且必须是TypeAdpater,JsonSerializer或JsonDeserializer这三个其中之一。


上面说JsonSerializer和JsonDeserializer都要配合GsonBuilder.registerTypeAdapter使用,但每次使用都要注册也太麻烦了,JsonAdapter就是为了解决这个痛点的。


使用方法(以User为例):


@JsonAdapter(UserTypeAdapter.class) //加在类上

public class User {

public User() {

}

public User(String name, int age) {

this.name = name;

this.age = age;

}

public User(String name, int age, String email) {

this.name = name;

this.age = age;

this.email = email;

}

public String name;

public int age;

@SerializedName(value = "emailAddress")

public String email;

}


使用时不用再使用 GsonBuilder去注册UserTypeAdapter了。


注:@JsonAdapter 仅支持 TypeAdapter或TypeAdapterFactory


Gson gson = new Gson();

User user = new User("怪盗kidou", 24, "[email protected]");

System.out.println(gson.toJson(user));

//结果:{"name":"怪盗kidou","age":24,"email":"[email protected]"}

//为区别结果,特意把email字段与@SerializedName注解中设置的不一样


注意:JsonAdapter的优先级比GsonBuilder.registerTypeAdapter的优先级更高。


五、TypeAdapter与 JsonSerializer、JsonDeserializer对比



六、TypeAdapter实例


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


这里的TypeAdapter 上面讲了一个自动将 字符串形式的数值转换成int型时可能出现 空字符串的问题,下面介绍一个其它读者的需求:


服务器返回的数据中data字段类型不固定,比如请求成功data是一个List,不成功的时候是String类型,这样前端在使用泛型解析的时候,怎么去处理呢?


其实这个问题的原因主要由服务器端造成的,接口设计时没有没有保证数据的一致性,正确的数据返回姿势:同一个接口任何情况下不得改变返回类型,要么就不要返,要么就返空值,如null、[],{}。


但这里还是给出解决方案:


方案一:


Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer >() {

@Override

public List> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

if (json.isJsonArray()){

//这里要自己负责解析了

Gson newGson = new Gson();

return newGson.fromJson(json,typeOfT);







请到「今天看啥」查看全文