Android Gson自定义类型适配器的高级应用场景原理剖析
一、自定义类型适配器概述
1.1 自定义类型适配器的核心作用
在Android开发中,Gson的默认序列化和反序列化机制虽然能处理大部分常见数据类型,但在面对复杂业务需求时存在局限性。自定义类型适配器(TypeAdapter)作为Gson扩展性的核心,能够实现以下功能:
- 对特殊数据类型(如日期、枚举、自定义复合类型)进行特定格式转换
- 处理JSON与Java对象之间的结构差异
- 实现数据的加密与解密
- 动态生成或修改序列化后的JSON内容
- 优化复杂对象的序列化性能
1.2 自定义类型适配器的实现基础
Gson的自定义类型适配器基于以下关键元素:
- TypeAdapter接口:定义了序列化(
write)和反序列化(read)的核心方法。 - TypeAdapterFactory工厂:用于动态创建TypeAdapter实例。
- @JsonAdapter注解:提供便捷的方式指定自定义适配器。
- 反射机制:在处理自定义对象时用于访问和操作字段。
二、自定义类型适配器的基础实现原理
2.1 TypeAdapter接口定义与核心方法
TypeAdapter接口是自定义类型适配的基础,其源码定义如下:
public abstract class TypeAdapter<T> {
// 将Java对象写入JsonWriter,转化为JSON格式
public abstract void write(JsonWriter out, T value) throws IOException;
// 从JsonReader读取JSON数据,转化为Java对象
public abstract T read(JsonReader in) throws IOException;
// 创建支持null值处理的TypeAdapter包装类
public TypeAdapter<T> nullSafe() {
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
} else {
TypeAdapter.this.write(out, value);
}
}
@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return TypeAdapter.this.read(in);
}
};
}
}
其中,write方法负责将Java对象转换为JSON格式写入JsonWriter,read方法则从JsonReader读取JSON数据并构建Java对象。nullSafe方法用于创建一个能自动处理null值的适配器包装类。
2.2 自定义TypeAdapter的基本实现步骤
以日期类型为例,展示自定义TypeAdapter的实现过程:
- 继承TypeAdapter接口:
public class DateTypeAdapter extends TypeAdapter<Date> {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private final SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
// 序列化:将Date对象转换为指定格式的字符串写入JSON
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String dateStr = formatter.format(value);
out.value(dateStr);
}
// 反序列化:将JSON字符串解析为Date对象
@Override
public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String dateStr = in.nextString();
try {
return formatter.parse(dateStr);
} catch (ParseException e) {
throw new JsonSyntaxException(e);
}
}
}
- 注册适配器:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateTypeAdapter())
.create();
2.3 适配器的注册与生效机制
Gson通过GsonBuilder注册自定义适配器,其核心逻辑如下:
public class GsonBuilder {
private final List<TypeAdapterFactory> factories = new ArrayList<>();
// 注册单个TypeAdapter
public GsonBuilder registerTypeAdapter(Type type, TypeAdapter<?> adapter) {
factories.add(TypeAdapters.newFactoryWithMatchRawType(type, adapter));
return this;
}
// 注册TypeAdapterFactory工厂
public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
factories.add(factory);
return this;
}
// 创建Gson实例,将注册的工厂链传入
public Gson create() {
return new Gson(this);
}
}
在创建Gson实例时,注册的适配器或工厂会加入到factories列表中。当执行序列化或反序列化操作时,Gson会按顺序遍历工厂链,尝试为目标类型创建对应的TypeAdapter。
三、自定义类型适配器的高级应用场景
3.1 复杂对象结构的处理
3.1.1 嵌套对象的序列化与反序列化
当Java对象包含多层嵌套结构时,自定义适配器可确保结构正确转换。例如,处理包含嵌套列表的对象:
public class NestedObject {
private List<List<String>> nestedList;
// 省略getter和setter
}
public class NestedObjectAdapter extends TypeAdapter<NestedObject> {
@Override
public void write(JsonWriter out, NestedObject value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginArray();
for (List<String> innerList : value.getNestedList()) {
out.beginArray();
for (String element : innerList) {
out.value(element);
}
out.endArray();
}
out.endArray();
}
@Override
public NestedObject read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
NestedObject object = new NestedObject();
List<List<String>> nestedList = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
List<String> innerList = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
innerList.add(in.nextString());
}
in.endArray();
nestedList.add(innerList);
}
in.endArray();
object.setNestedList(nestedList);
return object;
}
}
通过递归处理嵌套结构,确保JSON与Java对象的结构一致。
3.1.2 异构集合的处理
对于包含不同类型元素的集合(如List<Object>),可通过自定义适配器进行类型判断和转换:
public class HeterogeneousListAdapter extends TypeAdapter<List<Object>> {
private final Gson gson;
public HeterogeneousListAdapter(Gson gson) {
this.gson = gson;
}
@Override
public void write(JsonWriter out, List<Object> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginArray();
for (Object element : value) {
if (element == null) {
out.nullValue();
} else {
TypeAdapter<?> adapter = gson.getAdapter(element.getClass());
adapter.write(out, element);
}
}
out.endArray();
}
@Override
public List<Object> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
List<Object> list = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
JsonElement jsonElement = new JsonParser().parse(in);
Type type = getTypeByJsonElement(jsonElement);
TypeAdapter<?> adapter = gson.getAdapter(type);
Object element = adapter.fromJsonTree(jsonElement);
list.add(element);
}
in.endArray();
return list;
}
private Type getTypeByJsonElement(JsonElement jsonElement) {
// 根据JSON元素类型判断对应的Java类型
if (jsonElement.isJsonPrimitive()) {
JsonPrimitive primitive = jsonElement.getAsJsonPrimitive();
if (primitive.isBoolean()) {
return boolean.class;
} else if (primitive.isNumber()) {
return double.class;
} else if (primitive.isString()) {
return String.class;
}
} else if (jsonElement.isJsonArray()) {
return List.class;
} else if (jsonElement.isJsonObject()) {
return Object.class;
}
return null;
}
}
通过动态获取元素类型并调用相应的适配器,实现异构集合的正确处理。
3.2 数据的加密与解密
在涉及敏感数据传输或存储时,可通过自定义适配器实现数据的加密与解密:
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
public class EncryptedDataAdapter extends TypeAdapter<String> {
private static final String PASSWORD = "mysecretpassword";
private static final byte[] SALT = {1, 2, 3, 4, 5, 6, 7, 8};
private static final int ITERATIONS = 1000;
@Override
public void write(JsonWriter out, String value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
try {
String encryptedValue = encrypt(value);
out.value(encryptedValue);
} catch (Exception e) {
throw new IOException("Encryption failed", e);
}
}
@Override
public String read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
String encryptedValue = in.nextString();
return decrypt(encryptedValue);
} catch (Exception e) {
throw new IOException("Decryption failed", e);
}
}
private String encrypt(String plainText) throws Exception {
KeySpec spec = new PBEKeySpec(PASSWORD.toCharArray(), SALT, ITERATIONS);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = factory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
PBEParameterSpec paramSpec = new PBEParameterSpec(SALT, ITERATIONS);
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes());
return new String(encrypted);
}
private String decrypt(String encryptedText) throws Exception {
KeySpec spec = new PBEKeySpec(PASSWORD.toCharArray(), SALT, ITERATIONS);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = factory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
PBEParameterSpec paramSpec = new PBEParameterSpec(SALT, ITERATIONS);
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
byte[] decrypted = cipher.doFinal(encryptedText.getBytes());
return new String(decrypted);
}
}
通过在write方法中加密数据,在read方法中解密数据,确保敏感信息的安全性。
3.3 动态生成或修改JSON内容
自定义适配器可用于在序列化过程中动态生成或修改JSON内容。例如,为JSON对象添加时间戳字段:
public class TimestampAdapter extends TypeAdapter<Object> {
private final Gson gson;
public TimestampAdapter(Gson gson) {
this.gson = gson;
}
@Override
public void write(JsonWriter out, Object value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
JsonObject jsonObject = gson.toJsonTree(value).getAsJsonObject();
jsonObject.addProperty("timestamp", System.currentTimeMillis());
gson.getAdapter(Object.class).write(out, jsonObject);
}
@Override
public Object read(JsonReader in) throws IOException {
return gson.getAdapter(Object.class).read(in);
}
}
通过先将对象转换为JsonObject,添加自定义字段后再进行序列化,实现动态修改JSON内容。
3.4 性能优化场景
在处理大规模数据或频繁序列化的场景下,自定义适配器可优化性能。例如,减少反射调用:
public class User {
private String name;
private int age;
// 省略getter和setter
}
public class UserPerformanceAdapter extends TypeAdapter<User> {
@Override
public void write(JsonWriter out, User value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
out.name("name").value(value.getName());
out.name("age").value(value.getAge());
out.endObject();
}
@Override
public User read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
User user = new User();
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
if ("name".equals(name)) {
user.setName(in.nextString());
} else if ("age".equals(name)) {
user.setAge(in.nextInt());
} else {
in.skipValue();
}
}
in.endObject();
return user;
}
}
直接操作字段而不依赖反射,减少性能开销。
四、自定义类型适配器与其他扩展机制的结合
4.1 与@JsonAdapter注解结合
@JsonAdapter注解提供了便捷的方式指定自定义适配器:
@JsonAdapter(DateTypeAdapter.class)
public class MyModel {
private Date createdAt;
// 其他字段
}
Gson通过JsonAdapterAnnotationTypeAdapterFactory解析注解并创建适配器实例:
final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
JsonAdapter annotation = typeToken.getRawType().getAnnotation(JsonAdapter.class);
if (annotation == null) {
return null;
}
return createTypeAdapter(constructorConstructor, gson, typeToken, annotation);
}
private <T> TypeAdapter<T> createTypeAdapter(
ConstructorConstructor constructorConstructor,
Gson gson,
TypeToken<T> typeToken,
JsonAdapter annotation) {
Class<?> adapterClass = annotation.value();
if (TypeAdapter.class.isAssignableFrom(adapterClass)) {
Class<TypeAdapter<?>> typeAdapterClass = (Class<TypeAdapter<?>>) adapterClass;
ObjectConstructor<TypeAdapter<?>> constructor = constructorConstructor.get(TypeToken.get(typeAdapterClass));
TypeAdapter<?> typeAdapter = constructor.construct();
return (TypeAdapter<T>) typeAdapter.nullSafe();
}
// 处理JsonSerializer/JsonDeserializer的逻辑
// ...
throw new IllegalArgumentException("@JsonAdapter value must be a valid adapter type");
}
}
4.2 与TypeAdapterFactory结合
通过自定义TypeAdapterFactory,可以更灵活地创建适配器:
public class CustomTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
if (rawType == Date.class) {
return (TypeAdapter<T>) new DateTypeAdapter();
}
return null;
}
}
注册工厂后,Gson在创建适配器时会调用该工厂:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new CustomTypeAdapterFactory())
.create();
4.3 与JsonSerializer/JsonDeserializer结合
虽然TypeAdapter功能更全面,但JsonSerializer和JsonDeserializer也可与自定义适配器配合使用:
public class UserSerializer implements JsonSerializer<User> {
@Override
public JsonElement serialize(User user, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("name", user.getName());
jsonObject.addProperty("age", user.getAge());
return jsonObject;
}
}
public class UserDeserializer implements JsonDeserializer<User> {
@Override
public User deserialize(JsonElement json, Type
接上:
public class UserDeserializer implements JsonDeserializer<User> {
@Override
public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
User user = new User();
user.setName(jsonObject.get("name").getAsString());
user.setAge(jsonObject.get("age").getAsInt());
return user;
}
}
可以通过将 JsonSerializer 和 JsonDeserializer 封装到 TypeAdapter 中,实现统一处理:
public class UserJsonAdapter extends TypeAdapter<User> {
private final JsonSerializer<User> serializer;
private final JsonDeserializer<User> deserializer;
public UserJsonAdapter(JsonSerializer<User> serializer, JsonDeserializer<User> deserializer) {
this.serializer = serializer;
this.deserializer = deserializer;
}
@Override
public void write(JsonWriter out, User value) throws IOException {
JsonElement jsonElement = serializer.serialize(value, User.class, null);
new JsonTreeWriter(out).value(jsonElement);
}
@Override
public User read(JsonReader in) throws IOException {
JsonElement jsonElement = new JsonParser().parse(in);
return deserializer.deserialize(jsonElement, User.class, null);
}
}
五、自定义类型适配器的高级应用实践细节
5.1 处理多态类型
在实际开发中,经常会遇到多态类型的序列化和反序列化问题。例如,有一个 Animal 类及其子类 Dog 和 Cat:
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
为了正确处理这种多态结构,可以在自定义适配器中添加类型标识字段:
public class AnimalAdapter extends TypeAdapter<Animal> {
private static final String TYPE_FIELD = "type";
private static final String DOG_TYPE = "dog";
private static final String CAT_TYPE = "cat";
@Override
public void write(JsonWriter out, Animal value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
if (value instanceof Dog) {
out.name(TYPE_FIELD).value(DOG_TYPE);
} else if (value instanceof Cat) {
out.name(TYPE_FIELD).value(CAT_TYPE);
}
out.name("name").value(value.name);
out.endObject();
}
@Override
public Animal read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
in.beginObject();
String type = null;
String name = null;
while (in.hasNext()) {
String fieldName = in.nextName();
if (TYPE_FIELD.equals(fieldName)) {
type = in.nextString();
} else if ("name".equals(fieldName)) {
name = in.nextString();
} else {
in.skipValue();
}
}
in.endObject();
if (DOG_TYPE.equals(type)) {
return new Dog(name);
} else if (CAT_TYPE.equals(type)) {
return new Cat(name);
}
return new Animal(name);
}
}
注册适配器后,Gson 就能正确处理不同子类的序列化和反序列化,通过 type 字段来区分具体的类型。
5.2 处理循环引用
在复杂对象图中,循环引用是常见问题,自定义适配器可以通过记录已处理对象来避免无限递归:
import java.util.IdentityHashMap;
import java.util.Map;
class Node {
String data;
Node next;
public Node(String data) {
this.data = data;
}
}
public class NodeAdapter extends TypeAdapter<Node> {
private final Map<Node, Void> visited = new IdentityHashMap<>();
@Override
public void write(JsonWriter out, Node value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
if (visited.containsKey(value)) {
out.nullValue();
return;
}
visited.put(value, null);
out.beginObject();
out.name("data").value(value.data);
if (value.next != null) {
out.name("next");
write(out, value.next);
} else {
out.name("next").nullValue();
}
out.endObject();
visited.remove(value);
}
@Override
public Node read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
in.beginObject();
String data = null;
Node next = null;
while (in.hasNext()) {
String fieldName = in.nextName();
if ("data".equals(fieldName)) {
data = in.nextString();
} else if ("next".equals(fieldName)) {
next = read(in);
} else {
in.skipValue();
}
}
in.endObject();
Node node = new Node(data);
node.next = next;
return node;
}
}
通过使用 IdentityHashMap 记录已处理的对象,在序列化时如果再次遇到相同对象则写入 null,避免了循环引用导致的无限递归问题 。
5.3 与数据校验结合
在反序列化过程中,可以通过自定义适配器进行数据校验:
class Email {
private String address;
public Email(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
}
public class EmailAdapter extends TypeAdapter<Email> {
private static final String EMAIL_PATTERN =
"^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
@Override
public void write(JsonWriter out, Email value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.value(value.getAddress());
}
@Override
public Email read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String email = in.nextString();
if (!email.matches(EMAIL_PATTERN)) {
throw new JsonParseException("Invalid email format: " + email);
}
return new Email(email);
}
}
在 read 方法中,对读取到的邮箱地址进行格式校验,如果不符合要求则抛出异常,确保反序列化后的数据是有效的 。
六、自定义类型适配器的性能优化与注意事项
6.1 性能优化策略
- 减少反射调用:如前文提到的
UserPerformanceAdapter,直接操作对象字段,避免反射带来的性能损耗。反射在获取字段、调用方法时需要进行大量的运行时查找和权限检查,而直接操作字段则更为高效。 - 缓存对象和资源:对于在适配器中频繁使用的对象(如
SimpleDateFormat),可以将其声明为成员变量并复用,避免重复创建。不过需要注意线程安全问题,例如SimpleDateFormat不是线程安全的,在多线程环境下可以考虑使用DateTimeFormatter等替代方案。 - 批量处理:在处理集合类型时,如果可能,尽量采用批量写入或读取的方式,减少方法调用次数。例如,在写入一个大型列表时,可以先构建一个
JsonArray,然后一次性写入,而不是逐个元素写入 。
6.2 常见问题与注意事项
null值处理:必须在write和read方法中妥善处理null值,避免出现NullPointerException。可以使用nullSafe方法来简化null值处理逻辑,但对于一些特殊场景,可能需要自定义更精细的null值处理方式。- 线程安全:如果自定义适配器会在多线程环境下使用,必须确保其线程安全性。例如,避免在适配器中使用非线程安全的成员变量,或者对共享资源进行适当的同步控制。
- 类型匹配准确性:确保自定义适配器处理的类型与实际传入的类型一致,否则可能会导致序列化或反序列化错误。在复杂的继承和泛型场景下,需要仔细检查类型的匹配情况。
- 适配器注册顺序:当存在多个适配器或工厂时,注册顺序会影响适配器的选择。Gson 会按照注册顺序依次尝试创建适配器,因此需要合理安排注册顺序,确保正确的适配器被优先使用。