如何使用Gson序列化包含抽象类引用的对象
解决Gson反序列化含抽象类值的Map问题
你的核心问题是Gson反序列化时无法识别Map<Upgrades, PlayerUpgrades>里的抽象类子类实例——因为JSON本身不携带Java类型信息,Gson不知道该把JSON对象实例化成哪个具体的PlayerUpgrades子类。下面给你两个实用方案,不用手动序列化DataObj的20多个字段:
方案一:用Gson官方多态扩展(RuntimeTypeAdapterFactory)
这是最省心的方式,借助Gson的扩展库实现自动类型识别,不用写大量自定义代码:
- 引入依赖(Maven示例)
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson-extras</artifactId> <version>2.10.1</version> <!-- 请使用和你当前Gson一致的版本 --> </dependency>
- 配置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();
- 修改子类添加标识字段
每个PlayerUpgrades子类里添加type字段,值和注册时的标识对应:
public class HealthUpgrade extends PlayerUpgrades { public String type = "health"; // 和注册的标识一致 // 其他字段和实现方法 }
序列化时Gson会自动把type字段写入JSON,反序列化时根据这个字段找到对应的子类实例化。
方案二:结合Map键自定义TypeAdapter(适合用Upgrades键关联子类的场景)
如果你想利用upgradesMap的键(Upgrades枚举)来确定子类,可以自定义TypeAdapter,直接通过键映射到对应的子类:
- 创建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); } }
- 注册TypeAdapter到Gson
Gson gson = new GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(PlayerUpgrades.class, new PlayerUpgradesTypeAdapter()) .create();
注意事项
- 两种方案都不需要手动序列化DataObj的20多个字段,Gson会自动处理其他可序列化字段。
- 使用RuntimeTypeAdapterFactory时,必须确保所有PlayerUpgrades子类都注册到工厂,否则反序列化会抛出类型未知的异常。
- 自定义TypeAdapter时,要做好异常处理,比如找不到对应子类时返回默认实例或抛出明确的业务异常。
内容的提问来源于stack exchange,提问作者IgnoreExeption
相关产品推荐
相关产品推荐

