码字不易,请大佬们点点关注,谢谢~
一、Gson自定义类型适配器概述
1.1 自定义类型适配器的作用
在Android开发中,Gson作为主流的JSON处理框架,默认提供了基础数据类型(如int、String、boolean)和常见集合类型(如List、Map)的序列化与反序列化支持。然而,当遇到以下场景时,默认的转换机制无法满足需求:
- 处理特殊格式的数据(如日期类型需要特定格式的字符串表示)
- 处理第三方库中不兼容的对象结构
- 对敏感数据进行加密/解密处理
- 简化复杂对象的转换逻辑
此时,就需要通过自定义类型适配器(Type Adapter)来实现个性化的数据转换逻辑,确保Gson能够准确地将Java对象序列化为JSON,以及将JSON反序列化为Java对象。
1.2 自定义类型适配器的核心接口
Gson中自定义类型适配器的核心接口是TypeAdapter,其定义如下:
public abstract class TypeAdapter<T> {
// 将Java对象写入JSON流,用于序列化
public abstract void write(JsonWriter out, T value) throws IOException;
// 从JSON流读取数据并转换为Java对象,用于反序列化
public abstract T read(JsonReader in) throws IOException;
}
开发者需要继承该抽象类,实现write和read方法,分别定义序列化和反序列化的具体逻辑。
1.3 自定义类型适配器的工作流程
自定义类型适配器在Gson中的工作流程如下:
- 注册适配器:开发者通过
GsonBuilder将自定义的类型适配器与特定的Java类型关联起来。 - 类型匹配:在进行序列化或反序列化时,Gson根据目标Java类型查找对应的类型适配器。
- 执行转换:找到匹配的类型适配器后,Gson调用其
write或read方法完成数据转换。 - 返回结果:转换完成后,将结果返回给调用方,完成整个数据转换过程。
二、自定义类型适配器的注册机制
2.1 通过GsonBuilder注册适配器
Gson提供了GsonBuilder类用于构建Gson实例,并允许开发者注册自定义类型适配器。注册的核心方法是registerTypeAdapter,其源码如下:
public final class GsonBuilder {
// 存储类型适配器的注册表
private final Map<TypeToken<?>, TypeAdapter<?>> typeAdapters = new LinkedHashMap<>();
// 注册类型适配器的方法
public GsonBuilder registerTypeAdapter(Type type, TypeAdapter<?> typeAdapter) {
// 将Type对象转换为TypeToken,用于更准确地表示类型
TypeToken<?> typeToken = TypeToken.get(type);
// 将类型与对应的适配器存入注册表
typeAdapters.put(typeToken, typeAdapter);
return this;
}
// 构建Gson实例的方法
public Gson create() {
return new Gson(this);
}
}
使用示例:
// 自定义一个处理User类型的适配器
class UserTypeAdapter extends TypeAdapter<User> {
@Override
public void write(JsonWriter out, User value) throws IOException {
// 自定义序列化逻辑
out.beginObject();
out.name("name").value(value.getName());
out.name("age").value(value.getAge());
out.endObject();
}
@Override
public User read(JsonReader in) throws IOException {
// 自定义反序列化逻辑
in.beginObject();
String name = null;
int age = 0;
while (in.hasNext()) {
String fieldName = in.nextName();
if ("name".equals(fieldName)) {
name = in.nextString();
} else if ("age".equals(fieldName)) {
age = in.nextInt();
} else {
in.skipValue();
}
}
in.endObject();
return new User(name, age);
}
}
// 注册适配器并构建Gson实例
Gson gson = new GsonBuilder()
.registerTypeAdapter(User.class, new UserTypeAdapter())
.create();
2.2 使用TypeAdapterFactory批量注册
当需要为多个相关类型注册适配器,或者根据类型的某些特征动态生成适配器时,可以使用TypeAdapterFactory接口。其定义如下:
public interface TypeAdapterFactory {
// 根据类型和Gson实例创建TypeAdapter的方法
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}
TypeAdapterFactory的实现类需要实现create方法,根据传入的TypeToken判断是否需要为该类型创建适配器。如果需要,则返回对应的TypeAdapter实例;否则返回null。
示例代码:
// 自定义TypeAdapterFactory实现类
class CustomTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// 判断是否为User类型
if (type.getRawType() == User.class) {
// 为User类型创建并返回适配器
@SuppressWarnings("unchecked")
TypeAdapter<T> result = (TypeAdapter<T>) new UserTypeAdapter();
return result;
}
// 其他类型返回null,使用默认处理
return null;
}
}
// 通过GsonBuilder注册TypeAdapterFactory
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new CustomTypeAdapterFactory())
.create();
在Gson构建过程中,会遍历所有注册的TypeAdapterFactory,依次调用create方法,直到找到匹配的适配器。
2.3 注册优先级与覆盖规则
Gson在处理类型适配器注册时,存在以下优先级和覆盖规则:
- 显式注册优先:通过
registerTypeAdapter方法显式注册的适配器优先级最高,会覆盖其他方式注册的适配器。 - TypeAdapterFactory的顺序:多个
TypeAdapterFactory按注册顺序依次调用,先注册的工厂优先尝试创建适配器。如果某个工厂返回非null的适配器,则后续工厂不再调用。 - 默认适配器:如果所有注册的适配器和工厂都无法匹配目标类型,Gson会使用默认的反射机制生成适配器(通过
ReflectiveTypeAdapterFactory)。
例如:
// 先注册TypeAdapterFactory
GsonBuilder builder = new GsonBuilder()
.registerTypeAdapterFactory(new CustomTypeAdapterFactory());
// 再显式注册User类型的适配器
builder.registerTypeAdapter(User.class, new AnotherUserTypeAdapter());
Gson gson = builder.create();
在上述代码中,最终User类型会使用AnotherUserTypeAdapter,因为显式注册的优先级高于TypeAdapterFactory。
三、自定义类型适配器的工作机制
3.1 序列化工作流程
当Gson进行序列化操作(如调用toJson方法)时,涉及自定义类型适配器的工作流程如下:
- 获取类型适配器:Gson根据待序列化对象的类型,从注册表中查找对应的
TypeAdapter。查找过程如下:- 首先检查是否存在显式注册的适配器(通过
registerTypeAdapter方法注册)。 - 如果不存在,则依次调用注册的
TypeAdapterFactory的create方法,尝试生成适配器。 - 如果仍未找到,则使用默认的反射机制生成适配器。
- 首先检查是否存在显式注册的适配器(通过
- 调用write方法:找到匹配的
TypeAdapter后,Gson调用其write方法,将Java对象转换为JSON格式。write方法内部通过JsonWriter将对象的字段写入JSON流。 - 递归处理嵌套对象:如果对象包含嵌套结构(如其他对象或集合),
write方法会递归调用对应类型的TypeAdapter的write方法,完成整个对象图的序列化。
以User类型为例,其序列化源码分析:
// Gson类的toJson方法
public String toJson(Object src) {
return toJson(src, Object.class);
}
public String toJson(Object src, Type typeOfSrc) {
// 获取对应类型的TypeAdapter
TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
StringWriter writer = new StringWriter();
try {
JsonWriter jsonWriter = new JsonWriter(writer);
jsonWriter.setLenient(lenient);
if (serializeNulls) {
jsonWriter.setSerializeNulls(true);
}
// 调用TypeAdapter的write方法进行序列化
adapter.write(jsonWriter, src);
jsonWriter.close();
} catch (IOException e) {
// 捕获并处理异常
throw new JsonIOException("Failed making JSON string", e);
}
return writer.toString();
}
// UserTypeAdapter的write方法
class UserTypeAdapter extends TypeAdapter<User> {
@Override
public void write(JsonWriter out, User value) throws IOException {
out.beginObject();
out.name("name").value(value.getName());
out.name("age").value(value.getAge());
out.endObject();
}
}
3.2 反序列化工作流程
当Gson进行反序列化操作(如调用fromJson方法)时,涉及自定义类型适配器的工作流程如下:
- 获取类型适配器:与序列化类似,Gson根据目标类型从注册表中查找对应的
TypeAdapter。 - 调用read方法:找到匹配的
TypeAdapter后,Gson调用其read方法,从JSON流中读取数据并转换为Java对象。read方法内部通过JsonReader解析JSON数据,并构建目标对象。 - 递归处理嵌套对象:如果JSON数据包含嵌套结构,
read方法会递归调用对应类型的TypeAdapter的read方法,逐步构建完整的对象图。
以User类型为例,其反序列化源码分析:
// Gson类的fromJson方法
public <T> T fromJson(String json, Type typeOfT) {
// 获取对应类型的TypeAdapter
TypeAdapter<T> adapter = getAdapter(TypeToken.get(typeOfT));
try {
JsonReader jsonReader = new JsonReader(new StringReader(json));
jsonReader.setLenient(lenient);
// 调用TypeAdapter的read方法进行反序列化
return adapter.read(jsonReader);
} catch (IOException e) {
// 捕获并处理异常
throw new JsonSyntaxException(json, e);
}
}
// UserTypeAdapter的read方法
class UserTypeAdapter extends TypeAdapter<User> {
@Override
public User read(JsonReader in) throws IOException {
in.beginObject();
String name = null;
int age = 0;
while (in.hasNext()) {
String fieldName = in.nextName();
if ("name".equals(fieldName)) {
name = in.nextString();
} else if ("age".equals(fieldName)) {
age = in.nextInt();
} else {
in.skipValue();
}
}
in.endObject();
return new User(name, age);
}
}
3.3 JsonReader与JsonWriter的协作
JsonReader和JsonWriter是Gson中用于读取和写入JSON数据的核心工具类,自定义类型适配器通过它们实现与JSON数据的交互:
- JsonReader:提供了一系列方法(如
nextString、nextInt、beginObject、endObject)用于解析JSON数据。在反序列化过程中,TypeAdapter的read方法通过JsonReader读取JSON字段,并将其转换为Java对象的属性值。
public class JsonReader {
// 读取字符串类型的字段值
public String nextString() throws IOException {
// 具体实现逻辑,解析JSON字符串
}
// 开始解析JSON对象
public void beginObject() throws IOException {
// 具体实现逻辑,处理对象开始标记
}
}
- JsonWriter:提供了对应的方法(如
value、name、beginObject、endObject)用于生成JSON数据。在序列化过程中,TypeAdapter的write方法通过JsonWriter将Java对象的属性写入JSON流。
public class JsonWriter {
// 写入字符串类型的字段值
public JsonWriter value(String value) throws IOException {
// 具体实现逻辑,写入JSON字符串
}
// 开始写入JSON对象
public JsonWriter beginObject() throws IOException {
// 具体实现逻辑,写入对象开始标记
}
}
四、自定义类型适配器的高级应用场景
4.1 处理日期类型
Java中的日期类型(如Date)在JSON中没有统一的表示方式,通过自定义类型适配器可以实现特定格式的日期序列化与反序列化。
示例代码:
// 自定义日期类型适配器
class DateTypeAdapter extends TypeAdapter<Date> {
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
// 将Date对象格式化为指定格式的字符串
String dateStr = sdf.format(value);
out.value(dateStr);
}
@Override
public Date read(JsonReader in) throws IOException {
String dateStr = in.nextString();
try {
// 将字符串解析为Date对象
return sdf.parse(dateStr);
} catch (ParseException e) {
throw new JsonSyntaxException("Invalid date format", e);
}
}
}
// 注册日期类型适配器
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateTypeAdapter())
.create();
4.2 数据加密与解密
在处理敏感数据(如用户密码)时,可以通过自定义类型适配器在序列化时对数据进行加密,在反序列化时进行解密。
示例代码:
// 自定义加密类型适配器
class EncryptedTypeAdapter extends TypeAdapter<String> {
private final EncryptionUtil encryptionUtil;
public EncryptedTypeAdapter(EncryptionUtil encryptionUtil) {
this.encryptionUtil = encryptionUtil;
}
@Override
public void write(JsonWriter out, String value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
// 对字符串进行加密
String encryptedValue = encryptionUtil.encrypt(value);
out.value(encryptedValue);
}
@Override
public String read(JsonReader in) throws IOException {
String encryptedValue = in.nextString();
// 对字符串进行解密
return encryptionUtil.decrypt(encryptedValue);
}
}
// 使用示例
EncryptionUtil encryptionUtil = new EncryptionUtil();
Gson gson = new GsonBuilder()
.registerTypeAdapter(String.class, new EncryptedTypeAdapter(encryptionUtil))
.create();
4.3 简化复杂对象转换
当对象结构复杂,默认的反射机制效率低下或无法满足需求时,可以通过自定义类型适配器简化转换逻辑。
例如,对于包含大量嵌套对象的Report类:
class Report {
private Header header;
private Body body;
private Footer footer;
// 省略构造方法和getter/setter
}
class Header {
private String title;
private String date;
// 省略构造方法和getter/setter
}
class Body {
private List<Section> sections;
// 省略构造方法和getter/setter
}
class Section {
private String title;
private String content;
// 省略构造方法和getter/setter
}
class Footer {
private String author;
private String signature;
// 省略构造方法和getter/setter
}
可以通过自定义类型适配器简化其转换过程:
class ReportTypeAdapter extends TypeAdapter<Report> {
@Override
public void write(JsonWriter out, Report value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
// 序列化Header
out.name("header");
writeHeader(out, value.getHeader());
// 序列化Body
out.name("body");
writeBody(out, value.getBody());
// 序列化Footer
out.name("footer");
writeFooter(out, value.getFooter());
out.endObject();
}
private void writeHeader(JsonWriter out, Header header) throws IOException {
if (header == null) {
out.nullValue();
return;
}
out.beginObject();
out.name("title").value(header.getTitle());
out.name("date").value(header.getDate());
out.endObject();
}
private void writeBody(JsonWriter out, Body body) throws IOException {
if (body == null) {
out.nullValue();
return;
}
out.beginObject();
out.name("sections");
writeSections(out, body.getSections());
out.endObject();
}
private void writeSections(JsonWriter out, List<Section> sections) throws IOException {
if (
五、自定义类型适配器与TypeAdapterFactory的深度协作
5.1 TypeAdapterFactory的链式调用机制
在Gson中,多个TypeAdapterFactory注册后会形成一条处理链。当Gson需要为某个类型寻找适配器时,会按照注册顺序依次调用每个TypeAdapterFactory的create方法,这种链式调用机制确保了灵活的类型适配扩展。
// Gson类中获取TypeAdapter的关键方法
public <T> TypeAdapter<T> getAdapter(TypeToken<T> typeToken) {
// 从缓存中查找TypeAdapter
TypeAdapter<?> cached = typeTokenCache.get(typeToken);
if (cached != null) {
@SuppressWarnings("unchecked")
TypeAdapter<T> result = (TypeAdapter<T>) cached;
return result;
}
// 遍历注册的TypeAdapterFactory
for (TypeAdapterFactory factory : factories) {
// 尝试通过工厂创建TypeAdapter
TypeAdapter<?> adapter = factory.create(this, typeToken);
if (adapter != null) {
// 将创建的适配器存入缓存
typeTokenCache.put(typeToken, adapter);
@SuppressWarnings("unchecked")
TypeAdapter<T> result = (TypeAdapter<T>) adapter;
return result;
}
}
// 如果所有工厂都无法创建适配器,抛出异常
throw new IllegalArgumentException("Gson cannot handle " + typeToken.getType());
}
上述代码中,factories是一个存储TypeAdapterFactory的列表,Gson依次调用每个工厂的create方法,一旦某个工厂返回非空的TypeAdapter,则停止遍历,使用该适配器进行后续的数据转换操作。
5.2 动态生成TypeAdapter
TypeAdapterFactory的强大之处在于能够根据类型的特征动态生成TypeAdapter。例如,当处理一系列具有相同基类的自定义类型时,可以通过一个TypeAdapterFactory为这些类型生成对应的适配器。
// 自定义的通用TypeAdapterFactory
class GenericTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// 获取类型的原始类
Class<? super T> rawType = type.getRawType();
// 判断是否属于需要处理的类型家族
if (BaseClass.class.isAssignableFrom(rawType)) {
// 通过反射等方式动态生成TypeAdapter
return (TypeAdapter<T>) new DynamicTypeAdapter<>(gson, type);
}
return null;
}
}
// 动态生成的TypeAdapter示例
class DynamicTypeAdapter<T> extends TypeAdapter<T> {
private final Gson gson;
private final TypeToken<T> typeToken;
public DynamicTypeAdapter(Gson gson, TypeToken<T> typeToken) {
this.gson = gson;
this.typeToken = typeToken;
}
@Override
public void write(JsonWriter out, T value) throws IOException {
// 根据具体类型动态处理序列化逻辑
if (value == null) {
out.nullValue();
return;
}
// 示例:获取类型的字段并序列化
Field[] fields = typeToken.getRawType().getDeclaredFields();
out.beginObject();
for (Field field : fields) {
field.setAccessible(true);
try {
Object fieldValue = field.get(value);
if (fieldValue != null) {
out.name(field.getName());
// 递归调用Gson处理字段值的序列化
gson.getAdapter(TypeToken.get(field.getGenericType())).write(out, fieldValue);
}
} catch (IllegalAccessException e) {
throw new JsonIOException("Failed to serialize field", e);
}
}
out.endObject();
}
@Override
public T read(JsonReader in) throws IOException {
// 根据具体类型动态处理反序列化逻辑
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
// 通过反射创建对象实例
T instance = typeToken.getRawType().getConstructor().newInstance();
in.beginObject();
while (in.hasNext()) {
String fieldName = in.nextName();
// 查找对象对应的字段
Field field = findField(typeToken.getRawType(), fieldName);
if (field != null) {
field.setAccessible(true);
Type fieldType = field.getGenericType();
// 递归调用Gson处理字段值的反序列化
Object fieldValue = gson.getAdapter(TypeToken.get(fieldType)).read(in);
field.set(instance, fieldValue);
} else {
in.skipValue();
}
}
in.endObject();
return instance;
} catch (Exception e) {
throw new JsonSyntaxException("Failed to deserialize object", e);
}
}
// 辅助方法:查找对象的字段
private Field findField(Class<?> clazz, String fieldName) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().equals(fieldName)) {
return field;
}
}
if (clazz.getSuperclass() != null) {
return findField(clazz.getSuperclass(), fieldName);
}
return null;
}
}
在上述代码中,GenericTypeAdapterFactory根据类型是否继承自BaseClass,动态生成对应的DynamicTypeAdapter,该适配器通过反射机制,在运行时处理对象的字段序列化与反序列化,实现了灵活的类型适配。
5.3 与默认TypeAdapterFactory的交互
Gson内部存在多个默认的TypeAdapterFactory,如ReflectiveTypeAdapterFactory(用于基于反射生成适配器)、CollectionTypeAdapterFactory(处理集合类型)、MapTypeAdapterFactory(处理映射类型)等。自定义的TypeAdapterFactory与这些默认工厂共同协作,形成完整的类型适配体系。
当自定义TypeAdapterFactory无法处理某个类型时,Gson会继续调用后续的工厂,直到找到合适的适配器或使用默认的反射机制生成适配器。例如,在处理一个包含自定义类型的集合时:
// 假设自定义类型MyCustomType
class MyCustomType {
private String customField;
// 省略构造方法和getter/setter
}
// 自定义处理MyCustomType的TypeAdapterFactory
class MyCustomTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (type.getRawType() == MyCustomType.class) {
return (TypeAdapter<T>) new MyCustomTypeAdapter();
}
return null;
}
}
class MyCustomTypeAdapter extends TypeAdapter<MyCustomType> {
@Override
public void write(JsonWriter out, MyCustomType value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
out.name("customField").value(value.getCustomField());
out.endObject();
}
@Override
public MyCustomType read(JsonReader in) throws IOException {
in.beginObject();
String customField = null;
while (in.hasNext()) {
String fieldName = in.nextName();
if ("customField".equals(fieldName)) {
customField = in.nextString();
} else {
in.skipValue();
}
}
in.endObject();
return new MyCustomType(customField);
}
}
// 注册自定义工厂并处理集合
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyCustomTypeAdapterFactory())
.create();
List<MyCustomType> myCustomTypeList = new ArrayList<>();
// 添加数据...
String json = gson.toJson(myCustomTypeList);
在序列化List<MyCustomType>时,Gson首先调用CollectionTypeAdapterFactory,该工厂在处理集合元素类型时,会触发自定义的MyCustomTypeAdapterFactory为MyCustomType创建适配器,从而实现对自定义类型集合的正确处理。
六、自定义类型适配器的性能优化策略
6.1 减少反射调用
在自定义类型适配器中,频繁使用反射(如通过Class.getDeclaredFields获取字段、通过Constructor.newInstance创建对象)会带来性能开销。可以通过以下方式减少反射:
- 缓存反射结果:将反射获取的字段、构造函数等信息进行缓存,避免重复获取。
class CachedReflectionAdapter extends TypeAdapter<MyClass> {
private Constructor<MyClass> constructor;
private Map<String, Field> fieldMap;
public CachedReflectionAdapter() {
try {
Class<MyClass> clazz = MyClass.class;
constructor = clazz.getConstructor();
Field[] fields = clazz.getDeclaredFields();
fieldMap = new HashMap<>();
for (Field field : fields) {
field.setAccessible(true);
fieldMap.put(field.getName(), field);
}
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException("Failed to initialize adapter", e);
}
}
@Override
public void write(JsonWriter out, MyClass value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
try {
Object fieldValue = entry.getValue().get(value);
if (fieldValue != null) {
out.name(entry.getKey());
// 假设使用Gson处理字段值的序列化
Gson gson = new Gson();
gson.getAdapter(TypeToken.get(entry.getValue().getGenericType())).write(out, fieldValue);
}
} catch (IllegalAccessException e) {
throw new JsonIOException("Failed to serialize field", e);
}
}
out.endObject();
}
@Override
public MyClass read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
MyClass instance = constructor.newInstance();
in.beginObject();
while (in.hasNext()) {
String fieldName = in.nextName();
Field field = fieldMap.get(fieldName);
if (field != null) {
field.setAccessible(true);
Type fieldType = field.getGenericType();
// 假设使用Gson处理字段值的反序列化
Object fieldValue = new Gson().getAdapter(TypeToken.get(fieldType)).read(in);
field.set(instance, fieldValue);
} else {
in.skipValue();
}
}
in.endObject();
return instance;
} catch (Exception e) {
throw new JsonSyntaxException("Failed to deserialize object", e);
}
}
}
- 使用代码生成工具:借助代码生成工具(如Annotation Processor),在编译期生成特定类型的适配器代码,避免运行时反射。
6.2 复用TypeAdapter实例
在处理大量相同类型对象的序列化或反序列化时,复用TypeAdapter实例可以减少对象创建开销。可以将TypeAdapter实例进行缓存管理。
class TypeAdapterCache {
private static final Map<TypeToken<?>, TypeAdapter<?>> cache = new HashMap<>();
public static <T> TypeAdapter<T> get(TypeToken<T> typeToken) {
return (TypeAdapter<T>) cache.computeIfAbsent(typeToken, k -> {
// 假设这里通过Gson获取适配器
Gson gson = new Gson();
return gson.getAdapter(k);
});
}
}
// 使用示例
TypeToken<MyClass> typeToken = TypeToken.get(MyClass.class);
TypeAdapter<MyClass> adapter = TypeAdapterCache.get(typeToken);
MyClass myObject = adapter.fromJson(jsonData);
6.3 优化JsonReader与JsonWriter操作
JsonReader和JsonWriter的操作效率也会影响适配器性能。可以采取以下优化措施:
- 减少方法调用层级:避免在
write和read方法中进行过多的方法嵌套调用,尽量扁平化逻辑。 - 批量处理数据:对于集合类型的处理,尽量减少循环中单个元素处理的开销,考虑批量读取或写入数据。例如,在处理
List类型时,可以一次性读取多个元素后再进行转换,而不是逐个读取转换。
七、自定义类型适配器的错误处理与调试
7.1 常见异常类型及原因
在使用自定义类型适配器时,可能会遇到以下异常:
- JsonSyntaxException:通常在反序列化过程中出现,原因包括JSON格式错误、字段类型不匹配、缺少必要字段等。例如,在
read方法中,如果JSON数据中的字段类型与Java对象字段类型不一致,就会抛出该异常。
class MyAdapter extends TypeAdapter<MyObject> {
@Override
public void write(JsonWriter out, MyObject value) throws IOException {
// 序列化逻辑
}
@Override
public MyObject read(JsonReader in) throws IOException {
in.beginObject();
int requiredField = 0;
while (in.hasNext()) {
String fieldName = in.nextName();
if ("requiredField".equals(fieldName)) {
try {
requiredField = in.nextInt();
} catch (IOException e) {
// 如果读取的不是整数,会抛出JsonSyntaxException
throw new JsonSyntaxException("Expected integer for requiredField", e);
}
} else {
in.skipValue();
}
}
in.endObject();
return new MyObject(requiredField);
}
}
- JsonIOException:在序列化或反序列化过程中,由于I/O操作错误(如写入JSON流失败、读取JSON数据失败)导致。
- IllegalAccessException:当通过反射访问对象的私有字段或方法时,权限设置不正确会引发该异常。例如在自定义适配器中通过反射设置对象字段值时,如果字段不可访问就会抛出此异常。
7.2 调试技巧
- 日志输出:在
write和read方法中添加详细的日志输出,记录关键数据和操作步骤,便于定位问题。
class DebuggingAdapter extends TypeAdapter<MyData> {
private static final Logger logger = LoggerFactory.getLogger(DebuggingAdapter.class);
@Override
public void write(JsonWriter out, MyData value) throws IOException {
logger.debug("Serializing MyData object: {}", value);
out.beginObject();
// 序列化逻辑...
out.endObject();
logger.debug("Serialization completed");
}
@Override
public MyData read(JsonReader in) throws IOException {
logger.debug("Starting deserialization");
in.beginObject();
MyData data = new MyData();
while (in.hasNext()) {
String fieldName = in.nextName();
// 处理字段...
logger.debug("Processed field: {}", fieldName);
}
in.endObject();
logger.debug("Deserialization completed: {}", data);
return data;
}
}
- 单元测试:编写单元测试用例,针对自定义适配器的
write和read方法进行测试,通过不同的输入输出场景验证适配器的正确性。可以使用JUnit等测试框架,结合Mock对象模拟JSON数据的读取和写入操作。
import org.junit.Test;
import static org.junit.Assert.*;
public class MyAdapterTest {
@Test
public void testSerializationAndDeserialization() {
MyObject originalObject = new MyObject(10);
Gson gson = new GsonBuilder()
.registerTypeAdapter(MyObject.class, new MyAdapter())
.create();
String json = gson.toJson(originalObject);
MyObject deserializedObject = gson.fromJson(json, MyObject.class);
assertEquals(originalObject.getRequiredField(), deserializedObject.getRequiredField());
}
}