Gson
Gson 使用指南(一)
Gson 使用指南(二)
Gson使用指南(三)
Gson使用指南(四) TypeAdapter
序列化和反序列化
TypeAdapter
TypeAdapter
是Gson
自 2.0(源码注释上说的是2.1)
开始版本提供的一个抽象类
,用于接管某种类型的序列化和反序列化过程
,包含两个注要方法 write(JsonWriter,T)
和 read(JsonReader)
,其它的方法都是final方法
并 最终调用这两个抽象方法
write(JsonWriter out, T value) 抽象方法 参数是 JsonWriter 以及 java object。客户端用于进行写入操作
toJson(T value) 创建 JsonWriter 对象,然后将其和 value 一起传入 write 方法中 用于将Java Object转换为Json字符串
toJsonTree(T value) 创建 JsonTreeWriter 对象,然后将其和 value 一起传入 write 方法中 用于将Java Object转换为Json字符串
read(JsonReader in) 抽象方法 参数是 JsonReader。客户端用于 读取 操作
fromJson(Reader in) 创建 JsonReader 对象,将其传入 read 方法中 用于将Json字符串转换为Java Object
fromJsonTree(JsonElement jsonTree) 创建 JsonTreeReader 对象 将其传入 read 方法中 用于将Json字符串转换为Java Object
public abstract class TypeAdapter<T> {
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 配合使用
该抽象类
的实现子类
主要是在ReflectiveTypeAdapterFactory
,ObjectTypeAdapter
以及TypeAdapters
中
ReflectiveTypeAdapterFactory 是一个反射工厂类 主要是对自定义数据类型没有定义对应的解析器时
所以通过反射来进行属性的获取,同时自定义适配器进行数据的读写转换操作
ObjectTypeAdapter 表示一个 Json 对象,使用该 TypeAdapter 可以进行序列化和反序列化操作
TypeAdapters 是一个 TypeAdapter集合 ,帮助定义了一些数据类型的数据进行读写方式,无需客户端自己去实现
示例
User user = new User("怪盗kidou", 24);
user.emailAddress = "ikidou@example.com";
Gson gson = new GsonBuilder()
//为User注册TypeAdapter
.registerTypeAdapter(User.class, new UserTypeAdapter())
.create();
System.out.println(gson.toJson(user));
public class UserTypeAdapter extends TypeAdapter<User> {
@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
注册了 TypeAdapte
r之后,只要是操作 User.class
那些之前介绍的@SerializedName 、FieldNamingStrategy、Since、Until、Expos
通通都黯然失色,失去了效果,
只会调用我们实现的UserTypeAdapter.write(JsonWriter, User)
方法
gson 容错,处理返回的空字符串
再说一个场景,在该系列的第一篇文章就说到了 Gson有一定的容错机制
,比如将字符串 "24"
转成 int 的24
, 但如果有些情况下给你返了个空字符串怎么办
? 虽然这是服务器端的问题
int 型会出错
,注册一个TypeAdapter
把 序列化和反序列化的过程接管
不就行了
Gson gson = new GsonBuilder()
.registerTypeAdapter(Integer.class, new TypeAdapter<Integer>() {
@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<Integer>() {
@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<Number> numberJsonSerializer = new JsonSerializer<Number>() {
@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 的原因
换成 registerTypeHierarchyAdapter 就可以使用Number.class而不用一个一个的单独注册了
registerTypeHierarchyAdapter
registerTypeAdapter
与 registerTypeHierarchyAdapter
的区别
注:
如果一个被序列化的对象本身就带有泛型,且注册了相应的 TypeAdapter
那么必须调用 Gson.toJson(Object,Type),明确告诉 Gson 对象的类型
Type type = new TypeToken<List<User>>() {}.getType();
TypeAdapter typeAdapter = new TypeAdapter<List<User>>() {
//略
};
Gson gson = new GsonBuilder()
.registerTypeAdapter(type, typeAdapter)
.create();
List<User> list = new ArrayList<>();
list.add(new User("a",11));
list.add(new User("b",22));
//注意,多了个type参数
String result = gson.toJson(list, type);
TypeAdapterFactory
用于创建 TypeAdapter
的工厂类
,通过对比 Type
,确定有没有对应的 TypeAdapter
,没有就返回 null
,与GsonBuilder.registerTypeAdapterFactory
配合使用
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return null;
}
})
.create();
@JsonAdapter 注解
JsonAdapter
相较之前介绍的 SerializedName 、FieldNamingStrategy、Since、Until、Expos
这几个注解都是比较特殊的
其它的几个都是用在POJO的字段上
,而这一个是用在POJO类上的
接收一个参数
,且必须是 TypeAdpater,JsonSerializer或JsonDeserializer
这三个其中之一
上面说JsonSerializer
和 JsonDeserializer
都要 配合 GsonBuilder.registerTypeAdapter
使用,JsonAdapter
就是为了解决这个问题
@JsonAdapter(User.Adapter.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;
public static class Adapter extends TypeAdapter<User> {
@Override
public void write(final JsonWriter jsonWriter, final User user) throws IOException {
jsonWriter.value(enumeration.getValue());
}
@Override
public User read(final JsonReader jsonReader) throws IOException {
String value = jsonReader.nextString();
return User.fromValue(String.valueOf(value));
}
}
}
使用时不用再使用 GsonBuilder 去注册 UserTypeAdapter 了
注:
@JsonAdapter 仅支持 TypeAdapter或TypeAdapterFactory
( 2.7开始已经支持 JsonSerializer/JsonDeserializer)
Gson gson = new Gson();
User user = new User("怪盗kidou", 24, "ikidou@example.com");
System.out.println(gson.toJson(user));
//结果:{"name":"怪盗kidou","age":24,"email":"ikidou@example.com"}
//为区别结果,特意把email字段与@SerializedName注解中设置的不一样
JsonAdapter 的优先级比 GsonBuilder.registerTypeAdapter 的优先级更高
TypeAdapter与 JsonSerializer、JsonDeserializer对比
TypeAdapter实例
这里的 TypeAdapter 泛指 TypeAdapter、JsonSerializer 和 JsonDeserializer
服务器
返回的数据中 data
字段类型不固定
,比如请求成功 data 是一个 List
, 不成功的时候是 String 类型
,这样前端在使用泛型解析的时候,怎么去处理
其实这个问题的原因 主要由服务器端造成的 , 接口设计时没有没有保证数据的一致性 ,正确的数据返回姿势:
同一个接口任何情况下不得改变返回类型,要么就不要返,要么就返空值,如 null、[]、{}
方案一
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
@Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonArray()){
//这里要自己负责解析了
Gson newGson = new Gson();
return newGson.fromJson(json,typeOfT);
}else {
//和接口类型不符,返回空List
return Collections.EMPTY_LIST;
}
}
}).create();
方案二
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
@Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonArray()) {
JsonArray array = json.getAsJsonArray();
Type itemType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
List list = new ArrayList<>();
for (int i = 0; i < array.size(); i++) {
JsonElement element = array.get(i);
Object item = context.deserialize(element, itemType);
list.add(item);
}
return list;
} else {
//和接口类型不符,返回空List
return Collections.EMPTY_LIST;
}
}
}).create();
必须使用 registerTypeHierarchyAdapter 方法,不然对 List 的子类无效,但 如果 POJO 中都是使用 List,那么可以使用 registerTypeAdapter
对于是数组的情况,需要创建一个新的 Gson,不可以直接使用 context, 不然 gson 又会调我们自定义的JsonDeserializer 造成递归调用
方案二没有 重新创建 Gson,那么就需要提取出 List<E> 中 E 的类型,然后分别反序列化适合为 E
从效率上推荐方案二,免去重新实例化 Gson 和注册其它 TypeAdapter 的过程
示例
时间戳到Timestame类型转换
package com.kevin.luan.service;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
/**
* 实现一个自定义的实行适配器
* <p>
* 基于Google GSON 实现JSON解析
* </p>
*
* @author kevin LUAN
*
*/
public class GsonTypeAdapterDemo {
public static void main(String[] args) {
Gson gson = new GsonBuilder().registerTypeAdapter(Timestamp.class, new TimestampAdapter()).registerTypeAdapter(HttpProtocol.class, new HttpProtocolAdapter()).create();
String json = "{\"price\":\"1.1001\",\"times\":\"" + System.currentTimeMillis() + "\",\"protocol\":\"http://www.koudai.com/abc/test.do?url=abc\"}";
Test pojo = gson.fromJson(json, Test.class);
System.out.println("JSON TO POJO:" + pojo);
json = gson.toJson(pojo);
System.err.println("POJO TO JSON:" + json);
}
/**
* 实现一个类型适配器(TypeAdapter)
*
* @author kevin LUAN
*
*/
public static class TimestampAdapter implements JsonSerializer<Timestamp>, JsonDeserializer<Timestamp> {
@Override
public Timestamp deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
if (json != null) {
try {
return new Timestamp(json.getAsLong());
} catch (JsonParseException e) {
throw e;
}
}
return null;
}
@Override
public JsonElement serialize(Timestamp value, Type type, JsonSerializationContext context) {
if (value != null) {
return new JsonPrimitive(value.getTime());
}
return null;
}
}
}
Date类型转成json需要是long类型
自定义 adapter
集成 TypeAdapter
并覆盖 write 方法
,利用 jsonWriter.value()
方法将date对象转化成任意你想要的值作为json输出值
public class DateToLongAdapter extends TypeAdapter<Date> {
@Override
public void write(JsonWriter jsonWriter, Date date) throws IOException {
if (date == null) {
jsonWriter.value("");
return;
}
jsonWriter.value(date.getTime());
}
@Override
public Date read(JsonReader in) throws IOException {
return null;
}
}
通过 GsonBuilder
注册 TypeAdapter
GsonBuilder 类提供 注册 TypeAdapter 功能,将自定义 typeAdapter 注册到 Gson 对象 中
调用.Create()方法,该方法会返回一个Gson实例
该Gson实例可以做任何之前已经向你展示的功能:映射任何从JSON来或者到JSON去的数据
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Date.class, new DateToLongAdapter());
Gson gson = builder.create();
String msg = gson.toJson(notificationInfo);