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

如何使用Gson序列化包含抽象类引用的对象

解决Gson反序列化含抽象类值的Map问题

你的核心问题是Gson反序列化时无法识别Map<Upgrades, PlayerUpgrades>里的抽象类子类实例——因为JSON本身不携带Java类型信息,Gson不知道该把JSON对象实例化成哪个具体的PlayerUpgrades子类。下面给你两个实用方案,不用手动序列化DataObj的20多个字段:

方案一:用Gson官方多态扩展(RuntimeTypeAdapterFactory)

这是最省心的方式,借助Gson的扩展库实现自动类型识别,不用写大量自定义代码:

  1. 引入依赖(Maven示例)
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson-extras</artifactId>
    <version>2.10.1</version> <!-- 请使用和你当前Gson一致的版本 -->
</dependency>
  1. 配置RuntimeTypeAdapterFactory
    给抽象类PlayerUpgrades创建类型适配器工厂,指定一个用来区分子类的标识字段(比如type),然后注册所有子类:
// 假设你有两个子类:HealthUpgrade、SpeedUpgrade
RuntimeTypeAdapterFactory<PlayerUpgrades> typeAdapterFactory = 
    RuntimeTypeAdapterFactory.of(PlayerUpgrades.class, "type")
        .registerSubtype(HealthUpgrade.class, "health")
        .registerSubtype(SpeedUpgrade.class, "speed");

// 创建Gson实例时注册这个工厂
Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .registerTypeAdapterFactory(typeAdapterFactory)
    .create();
  1. 修改子类添加标识字段
    每个PlayerUpgrades子类里添加type字段,值和注册时的标识对应:
public class HealthUpgrade extends PlayerUpgrades {
    public String type = "health"; // 和注册的标识一致
    // 其他字段和实现方法
}

序列化时Gson会自动把type字段写入JSON,反序列化时根据这个字段找到对应的子类实例化。

方案二:结合Map键自定义TypeAdapter(适合用Upgrades键关联子类的场景)

如果你想利用upgradesMap的键(Upgrades枚举)来确定子类,可以自定义TypeAdapter,直接通过键映射到对应的子类:

  1. 创建PlayerUpgrades的TypeAdapter
public class PlayerUpgradesTypeAdapter extends TypeAdapter<PlayerUpgrades> {
    // 提前定义好Upgrades和子类的映射关系
    private static final Map<Upgrades, Class<? extends PlayerUpgrades>> UPGRADE_CLASS_MAP = new HashMap<>();
    static {
        UPGRADE_CLASS_MAP.put(Upgrades.HEALTH, HealthUpgrade.class);
        UPGRADE_CLASS_MAP.put(Upgrades.SPEED, SpeedUpgrade.class);
        // 其他键值映射
    }

    @Override
    public void write(JsonWriter out, PlayerUpgrades value) throws IOException {
        // 序列化时写入子类字段+对应的Upgrades键
        JsonObject obj = (JsonObject) new Gson().toJsonTree(value);
        // 先获取当前子类对应的Upgrades键
        Upgrades matchedKey = UPGRADE_CLASS_MAP.entrySet().stream()
                .filter(entry -> entry.getValue().equals(value.getClass()))
                .map(Map.Entry::getKey)
                .findFirst()
                .orElse(null);
        if (matchedKey != null) {
            obj.addProperty("upgradeKey", matchedKey.name());
        }
        new Gson().getAdapter(JsonObject.class).write(out, obj);
    }

    @Override
    public PlayerUpgrades read(JsonReader in) throws IOException {
        JsonObject obj = JsonParser.parseReader(in).getAsJsonObject();
        String upgradeKeyStr = obj.get("upgradeKey").getAsString();
        Upgrades upgradeKey = Upgrades.valueOf(upgradeKeyStr);
        Class<? extends PlayerUpgrades> clazz = UPGRADE_CLASS_MAP.get(upgradeKey);
        // 移除upgradeKey字段,避免反序列化子类时出现未知字段
        obj.remove("upgradeKey");
        return new Gson().fromJson(obj, clazz);
    }
}
  1. 注册TypeAdapter到Gson
Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .registerTypeAdapter(PlayerUpgrades.class, new PlayerUpgradesTypeAdapter())
    .create();

注意事项

  • 两种方案都不需要手动序列化DataObj的20多个字段,Gson会自动处理其他可序列化字段。
  • 使用RuntimeTypeAdapterFactory时,必须确保所有PlayerUpgrades子类都注册到工厂,否则反序列化会抛出类型未知的异常。
  • 自定义TypeAdapter时,要做好异常处理,比如找不到对应子类时返回默认实例或抛出明确的业务异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 11:35:31