TypeAdapter是Gson中实现序列化与反序列化完全自定义逻辑的最底层方式,需重写write和read方法以彻底控制JSON读写过程,并支持全局、泛型类型及局部三种注册方式。

在 Gson 中,TypeAdapter 是实现序列化与反序列化**完全自定义逻辑**最直接、最底层的扩展方式。它绕过了默认的反射机制和字段映射规则,让你能彻底控制对象如何转成 JSON 字符串、以及 JSON 如何还原为对象——包括字段名重命名、类型转换、空值策略、嵌套结构扁平化/展开等。
一、TypeAdapter 的核心作用:接管读写全过程
TypeAdapter<T> 是一个抽象类,需重写两个关键方法:
-
write(JsonWriter out, T value):决定对象value如何被写入 JSON(如跳过某些字段、改写字段名、手动拼接结构) -
read(JsonReader in):决定从 JSON 流中如何解析出T实例(如根据某个字段值动态选择子类型、处理缺失字段的默认值、解析非标准格式字符串为日期等)
它不依赖注解或反射,也不自动处理泛型擦除问题,因此适合需要极致控制或兼容特殊协议的场景。
二、注册 TypeAdapter 的三种常用方式
要让 Gson 使用你的 TypeAdapter,必须显式注册:
立即学习“Java免费学习笔记(深入)”;
-
全局注册(针对某类型所有使用):
Gson gson = new GsonBuilder().registerTypeAdapter(MyClass.class, new MyClassAdapter()).create(); -
带泛型的类型注册(如 List<User>):
Type userListType = new TypeToken<List<User>>(){}.getType();<br> gsonBuilder.registerTypeAdapter(userListType, new UserListAdapter()); -
局部临时使用(不注册,仅单次调用):
gson.getAdapter(MyClass.class).write(writer, obj);或gson.getAdapter(MyClass.class).read(reader);
三、实战示例:自定义时间格式 + 字段别名 + 空值忽略
假设有个类 Event,要求:
- 序列化时把
timestamp(long毫秒)转为"yyyy-MM-dd HH:mm:ss"字符串,并字段名改为"occurred_at" - 反序列化时支持两种输入:
"occurred_at": "2024-05-20 14:30:00"或"timestamp": 1716215400000 - 若 JSON 中缺失
occurred_at和timestamp,则设为当前时间
可这样写 TypeAdapter<Event>:
public class EventAdapter extends TypeAdapter<Event> {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void write(JsonWriter out, Event value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
out.name("id").value(value.getId());
out.name("name").value(value.getName());
// 自定义时间字段:转格式 + 改名
out.name("occurred_at").value(sdf.format(new Date(value.getTimestamp())));
out.endObject();
}
@Override
public Event read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
in.beginObject();
Long timestamp = null;
String occurredAtStr = null;
while (in.hasNext()) {
String name = in.nextName();
switch (name) {
case "id":
// ... 解析 id
break;
case "name":
// ... 解析 name
break;
case "occurred_at":
occurredAtStr = in.nextString();
break;
case "timestamp":
timestamp = in.nextLong();
break;
default:
in.skipValue();
}
}
in.endObject();
// 合并时间逻辑:优先用 occurred_at,否则用 timestamp,都无则用当前时间
if (occurredAtStr != null) {
try {
timestamp = sdf.parse(occurredAtStr).getTime();
} catch (ParseException ignored) { }
}
if (timestamp == null) {
timestamp = System.currentTimeMillis();
}
return new Event(/*...*/, timestamp);
}
}
四、注意事项与避坑点
注意以下细节,否则容易出现解析异常或静默失败:
-
必须严格匹配 JSON token 类型:调用
in.nextInt()前确保peek() == JsonToken.NUMBER,否则抛MalformedJsonException -
手动跳过未知字段:用
in.skipValue(),不要忽略未识别的nextName(),否则后续读取会错位 -
嵌套对象需递归调用适配器:比如字段是
User类型,应通过gson.getAdapter(User.class).read(in)而非手动解析 -
线程安全:
TypeAdapter实例建议无状态(stateless),避免共享可变成员变量;若需复用SimpleDateFormat,应加ThreadLocal或用DateTimeFormatter(Java 8+)


















