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

使用Gson-extras实现带transient变量的多态JSON反序列化问题

解决Gson-extras中RuntimeTypeAdapterFactory与PostConstructAdapterFactory共存时的多态序列化问题

我完全懂你遇到的这个棘手问题:当一个类同时具备多态特性和transient变量,并且作为其他类的属性进行序列化时,只要子类带有@PostConstruct注解,RuntimeTypeAdapterFactory负责生成的type字段就会凭空消失,直接导致多态反序列化失效。这本质上是两个AdapterFactory的逻辑冲突——PostConstructAdapterFactory在处理带有@PostConstruct的类时,完全跳过了RuntimeTypeAdapterFactory的多态处理逻辑,自己接管了序列化流程。

问题根源拆解

PostConstructAdapterFactory的默认实现会为带有@PostConstruct注解的类直接生成一个自定义TypeAdapter,不会复用RuntimeTypeAdapterFactory已经注册好的多态TypeAdapter。当子类以父类类型的身份作为其他类的属性被序列化时,PostConstructAdapterFactory的TypeAdapter会被优先选中,自然就跳过了RuntimeTypeAdapterFactory添加type字段的关键步骤。

解决方案:自定义融合型AdapterFactory

我们需要编写一个自定义的AdapterFactory,把RuntimeTypeAdapterFactory的多态处理逻辑和PostConstructAdapterFactory的初始化逻辑深度结合,确保两种功能同时生效。核心思路是:先让RuntimeTypeAdapterFactory生成处理多态的TypeAdapter,再用PostConstruct的逻辑包装这个Adapter,既保证序列化时保留type字段,又能在反序列化后执行@PostConstruct方法初始化transient变量。

步骤1:实现自定义CombinedTypeAdapterFactory

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.springframework.beans.factory.annotation.PostConstruct;

import java.io.IOException;
import java.lang.reflect.Method;

public class CombinedTypeAdapterFactory implements TypeAdapterFactory {

    private final RuntimeTypeAdapterFactory<?> runtimeFactory;

    public CombinedTypeAdapterFactory(RuntimeTypeAdapterFactory<?> runtimeFactory) {
        this.runtimeFactory = runtimeFactory;
    }

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // 先获取RuntimeTypeAdapterFactory生成的多态TypeAdapter
        TypeAdapter<T> runtimeAdapter = runtimeFactory.create(gson, type);
        if (runtimeAdapter == null) {
            return null;
        }

        // 检查当前类型是否有@PostConstruct方法(递归检查父类)
        Method postConstructMethod = findPostConstructMethod(type.getRawType());
        if (postConstructMethod == null) {
            return runtimeAdapter;
        }

        // 包装RuntimeTypeAdapter,添加PostConstruct逻辑
        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                // 复用RuntimeTypeAdapter的序列化逻辑,保证type字段被写入
                runtimeAdapter.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                // 先用RuntimeTypeAdapter反序列化出对象
                T instance = runtimeAdapter.read(in);
                // 执行@PostConstruct方法初始化transient变量
                try {
                    postConstructMethod.setAccessible(true);
                    postConstructMethod.invoke(instance);
                } catch (Exception e) {
                    throw new RuntimeException("Failed to invoke @PostConstruct method", e);
                }
                return instance;
            }
        };
    }

    private Method findPostConstructMethod(Class<?> clazz) {
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(PostConstruct.class)) {
                return method;
            }
        }
        // 递归检查父类,支持继承场景
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null && superClass != Object.class) {
            return findPostConstructMethod(superClass);
        }
        return null;
    }
}

步骤2:修改GsonUtils的配置

把原来分别注册两个Factory的方式,改为注册我们自定义的CombinedTypeAdapterFactory,同时传入RuntimeTypeAdapterFactory实例:

public class GsonUtils {
    private static final RuntimeTypeAdapterFactory<BaseClass> BASE_CLASS_RUNTIME_FACTORY = RuntimeTypeAdapterFactory.of(BaseClass.class);
    private static final GsonBuilder gsonBuilder = new GsonBuilder()
            .registerTypeAdapterFactory(new CombinedTypeAdapterFactory(BASE_CLASS_RUNTIME_FACTORY));

    // 提供注册子类的公共方法
    public static void registerBaseClassSubtype(Class<? extends BaseClass> subtype) {
        BASE_CLASS_RUNTIME_FACTORY.registerSubtype(subtype);
    }

    public static Gson getGson() {
        return gsonBuilder.create();
    }
}

步骤3:调整BaseClass的注册逻辑

原来的BaseClass构造函数中注册子类的方式可以简化,直接调用GsonUtils的方法即可:

public class BaseClass {
    public BaseClass() {
        // 注册当前子类到RuntimeTypeAdapterFactory
        GsonUtils.registerBaseClassSubtype(this.getClass());
    }
}

步骤4:验证WrapperClass场景

现在测试WrapperClass的序列化和反序列化,确认type字段存在且transient变量被正确初始化:

public class WrapperClass {
    protected BaseClass dc = new DerivedClass(6);
}

public static void main(String[] args) {
    Gson gson = GsonUtils.getGson();
    String serialized = gson.toJson(new WrapperClass());
    System.out.println("Serialized JSON: " + serialized);
    // 输出应包含"type":"DerivedClass"字段
    WrapperClass wc = gson.fromJson(serialized, WrapperClass.class);
    DerivedClass dc = (DerivedClass) wc.dc;
    System.out.println("Doubled value: " + dc.doubled); // 预期输出12
}

关键说明

  • 自定义Factory优先复用RuntimeTypeAdapterFactory的逻辑,从根源保证多态序列化时type字段不会丢失。
  • 仅在类存在@PostConstruct方法时才添加初始化逻辑,兼容没有该注解的多态类。
  • 递归查找父类的@PostConstruct方法,确保继承场景下的逻辑正常生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 10:33:15