TypeAdapter的使用动机
Gson在序列化和反序列化时,默认情况下,是按照POJO类的字段属性名和JSON串键进行一一映射匹配,然后把JSON串的键对应的值转换成POJO相同字段对应的值,反之亦然,在这个过程中有一个JSON串Key对应的Value和对象之间如何转换(序列化/反序列化)的问题。
以Date为例,在序列化和反序列化时,Gson默认使用java.text.DateFormat将Date对象进行序列化成日期字符串, 以及将日期字符串反序列化为Date对象。
这种默认的转换逻辑,大多数情况下能够满足需求,不过有时我们需要自定义这个转换的逻辑,比如在JSON串中定义的是日期的毫秒数,而POJO中定义的是Date类型,为了适配这种类型(字串符数字和Date)的转换,Gson提供了类型适配器。通过类型适配器这种扩展机制,Gson的使用者可以根据实际的需要对某个具体类型的序列化和反序列化的转换进行控制
Date和字符串日期毫秒数适配
将日期类型序列化和反序列化长整数字符串(串中以长整数毫秒数表示日期)
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.Date;
class LongDateTypeAdapter extends TypeAdapter<Date> {
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value.getTime());
}
}
@Override
public Date read(JsonReader in) throws IOException {
if (in.peek() == null) {
return null;
}
String str = in. nextString();
Date d = new Date(Long.parseLong(str));
return d;
}
}
class Model {
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
public class Test {
public static void main(String[] args) {
Date d = new Date();
Model m = new Model();
m.setDate(d);
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new LongDateTypeAdapter()).create();
String str = gson.toJson(m);
System.out.println(str); //{"date":1407921708088}
m = gson.fromJson(str, Model.class);
System.out.println(m.getDate().getTime()); //1407921708088
}
}
上例中,将Model对象序列化为{"date":1407921708088},然后把{"date":1407921708088}反序列化为Model对象,Gson通过自定义的类型适配器LongDateTypeAdapter将日期类型Date和Long进行了适配。
数字和Enum类型的适配
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
enum Status {
OK, ERROR, UNKNOWN;
}
class EnumTypeAdapter extends TypeAdapter<Status> {
@Override
public Status read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
return null;
} else {
int status = in.nextInt();
if (status == 0) {
return Status.OK;
} else if (status == 1) {
return Status.ERROR;
} else {
return Status.UNKNOWN;
}
}
}
@Override
public void write(JsonWriter out, Status value) throws IOException {
if (value == null) {
out.nullValue();
} else if (value == Status.OK) {
out.value(0);
} else if (value == Status.ERROR) {
out.value(1);
} else {
out.value(2);
}
}
}
public class Test {
public static void main(String[] args) {
Gson gson = new GsonBuilder().registerTypeAdapter(Status.class, new EnumTypeAdapter()).create();
String str = gson.toJson(Status.OK);
System.out.println(str);//0
Status status = gson.fromJson(str, Status.class);
System.out.println(status);//OK
}
}
Gson预定义适配器
上面说到了,Gson默认使用java.text.DateFormat对日期和字符串进行转换,那么这种转换逻辑是如何实现的?这是使用了Gson预定义的适配器DateTypeAdapter, Gson预定义适配器在第七部分进行分析