Gson TypeAdapter交叉引用问题:如何将嵌套B类序列化为字符串
问题描述
我为类A和类B分别实现了TypeAdapter<A>和TypeAdapter<B>,其中A类包含一个B类型的字段。需求如下:
- 序列化A时,把B的JSON序列化结果以字符串形式作为A对应字段的值
- 反序列化A时,读取该字符串并反序列化为B类实例
示例代码(存在待实现逻辑):
public class A { private final B b; // 构造器、getter等省略 } public class B { // 字段、构造器等省略 } public class ATypeAdapter extends TypeAdapter<A> { @Override public void write(JsonWriter out, A a) throws IOException { out.beginObject(); out.name("b_json_value").value(/* 此处需设置B类的JSON序列化字符串*/); out.endObject(); } @Override public A read(JsonReader in) throws IOException { in.beginObject(); String bJson = null; while (in.hasNext()) { if (in.nextName().equals("b_json_value")) { bJson = in.nextString(); // 此处需读取字符串并反序列化为B类实例 } } in.endObject(); return new A(/* 反序列化后的B实例 */); } }
之前尝试通过构造函数传入Gson实例获取TypeAdapter<B>,但因为Gson实例要在GsonBuilder配置完所有TypeAdapter后才能初始化,导致无法提前传入,该方案不可行。请问如何解决?
解决方案
方法1:使用TypeAdapterFactory(推荐)
通过TypeAdapterFactory可以在Gson初始化完成后,获取到已配置好的TypeAdapter<B>,再注入到ATypeAdapter中,完美避开提前初始化的问题。
实现步骤:
- 改造
ATypeAdapter,添加接收TypeAdapter<B>的构造器:
public class ATypeAdapter extends TypeAdapter<A> { private final TypeAdapter<B> bTypeAdapter; // 传入B的TypeAdapter public ATypeAdapter(TypeAdapter<B> bTypeAdapter) { this.bTypeAdapter = bTypeAdapter; } @Override public void write(JsonWriter out, A a) throws IOException { out.beginObject(); // 将B序列化为字符串:先写到StringWriter,再转为字符串 StringWriter sw = new StringWriter(); JsonWriter bWriter = new JsonWriter(sw); bTypeAdapter.write(bWriter, a.getB()); bWriter.flush(); out.name("b_json_value").value(sw.toString()); out.endObject(); } @Override public A read(JsonReader in) throws IOException { in.beginObject(); String bJson = null; while (in.hasNext()) { String name = in.nextName(); if ("b_json_value".equals(name)) { bJson = in.nextString(); } else { // 跳过未知字段,避免解析报错 in.skipValue(); } } in.endObject(); // 将字符串反序列化为B:从StringReader读取 B b = bTypeAdapter.fromJson(new StringReader(bJson)); return new A(b); } }
- 实现
TypeAdapterFactory来创建ATypeAdapter:
public class ATypeAdapterFactory implements TypeAdapterFactory { @SuppressWarnings("unchecked") @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { if (typeToken.getRawType() == A.class) { // 获取已配置好的B的TypeAdapter TypeAdapter<B> bTypeAdapter = gson.getAdapter(B.class); return (TypeAdapter<T>) new ATypeAdapter(bTypeAdapter); } // 不是A类则返回null,交给Gson默认处理 return null; } }
- 配置GsonBuilder时注册工厂:
Gson gson = new GsonBuilder() .registerTypeAdapter(B.class, new BTypeAdapter()) // 先注册B的TypeAdapter .registerTypeAdapterFactory(new ATypeAdapterFactory()) // 注册A的工厂 .create();
方法2:在ATypeAdapter中延迟获取TypeAdapter<B>
如果不想用工厂,可以让ATypeAdapter持有Gson实例,利用Gson自动注入自身的机制,在首次使用时延迟获取TypeAdapter<B>。
实现步骤:
- 修改
ATypeAdapter,添加Gson字段和延迟初始化逻辑:
public class ATypeAdapter extends TypeAdapter<A> { private transient Gson gson; private transient TypeAdapter<B> bTypeAdapter; // 无参构造器,供Gson实例化 public ATypeAdapter() {} // 延迟初始化bTypeAdapter private TypeAdapter<B> getBTypeAdapter() { if (bTypeAdapter == null) { bTypeAdapter = gson.getAdapter(B.class); } return bTypeAdapter; } // Gson会自动调用此方法注入自身实例 @Override public void setGson(Gson gson) { this.gson = gson; } @Override public void write(JsonWriter out, A a) throws IOException { out.beginObject(); StringWriter sw = new StringWriter(); JsonWriter bWriter = new JsonWriter(sw); getBTypeAdapter().write(bWriter, a.getB()); bWriter.flush(); out.name("b_json_value").value(sw.toString()); out.endObject(); } @Override public A read(JsonReader in) throws IOException { in.beginObject(); String bJson = null; while (in.hasNext()) { String name = in.nextName(); if ("b_json_value".equals(name)) { bJson = in.nextString(); } else { in.skipValue(); } } in.endObject(); B b = getBTypeAdapter().fromJson(new StringReader(bJson)); return new A(b); } }
- 配置GsonBuilder:
Gson gson = new GsonBuilder() .registerTypeAdapter(B.class, new BTypeAdapter()) .registerTypeAdapter(A.class, new ATypeAdapter()) .create();
说明
- 方法1更符合Gson的设计规范,解耦性更好,推荐使用
- 两种方法都避免了提前初始化Gson的问题,确保能获取到已配置好的
TypeAdapter<B>
内容的提问来源于stack exchange,提问作者Tartur
相关产品推荐
相关产品推荐

