Android Gson自定义类型适配器的高级应用场景原理剖析(23)

90 阅读11分钟

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格式写入JsonWriterread方法则从JsonReader读取JSON数据并构建Java对象。nullSafe方法用于创建一个能自动处理null值的适配器包装类。

2.2 自定义TypeAdapter的基本实现步骤

以日期类型为例,展示自定义TypeAdapter的实现过程:

  1. 继承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);
        }
    }
}
  1. 注册适配器
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功能更全面,但JsonSerializerJsonDeserializer也可与自定义适配器配合使用:

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

可以通过将 JsonSerializerJsonDeserializer 封装到 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 类及其子类 DogCat

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 性能优化策略

  1. 减少反射调用:如前文提到的 UserPerformanceAdapter,直接操作对象字段,避免反射带来的性能损耗。反射在获取字段、调用方法时需要进行大量的运行时查找和权限检查,而直接操作字段则更为高效。
  2. 缓存对象和资源:对于在适配器中频繁使用的对象(如 SimpleDateFormat),可以将其声明为成员变量并复用,避免重复创建。不过需要注意线程安全问题,例如 SimpleDateFormat 不是线程安全的,在多线程环境下可以考虑使用 DateTimeFormatter 等替代方案。
  3. 批量处理:在处理集合类型时,如果可能,尽量采用批量写入或读取的方式,减少方法调用次数。例如,在写入一个大型列表时,可以先构建一个 JsonArray,然后一次性写入,而不是逐个元素写入 。

6.2 常见问题与注意事项

  1. null 值处理:必须在 writeread 方法中妥善处理 null 值,避免出现 NullPointerException。可以使用 nullSafe 方法来简化 null 值处理逻辑,但对于一些特殊场景,可能需要自定义更精细的 null 值处理方式。
  2. 线程安全:如果自定义适配器会在多线程环境下使用,必须确保其线程安全性。例如,避免在适配器中使用非线程安全的成员变量,或者对共享资源进行适当的同步控制。
  3. 类型匹配准确性:确保自定义适配器处理的类型与实际传入的类型一致,否则可能会导致序列化或反序列化错误。在复杂的继承和泛型场景下,需要仔细检查类型的匹配情况。
  4. 适配器注册顺序:当存在多个适配器或工厂时,注册顺序会影响适配器的选择。Gson 会按照注册顺序依次尝试创建适配器,因此需要合理安排注册顺序,确保正确的适配器被优先使用。