You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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中,完美避开提前初始化的问题。

实现步骤:

  1. 改造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);
    }
}
  1. 实现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;
    }
}
  1. 配置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>。

实现步骤:

  1. 修改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);
    }
}
  1. 配置GsonBuilder:
Gson gson = new GsonBuilder()
        .registerTypeAdapter(B.class, new BTypeAdapter())
        .registerTypeAdapter(A.class, new ATypeAdapter())
        .create();
说明
  • 方法1更符合Gson的设计规范,解耦性更好,推荐使用
  • 两种方法都避免了提前初始化Gson的问题,确保能获取到已配置好的TypeAdapter<B>

内容的提问来源于stack exchange,提问作者Tartur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:45:32