Gson反序列化多态类型Map<String,List<? extends Mother>>报错求助
问题分析
你遇到的问题本质是Gson处理多态类型反序列化时的局限性:序列化后的JSON中没有保存Java对象的类型元数据,反序列化时Gson只能根据List<? extends Mother>的泛型上限创建Mother类的实例,而非Son或Daughter子类,因此强制类型转换时抛出ClassCastException。
解决方案1:自定义TypeAdapter+类型标识字段
通过在父类中添加类型标识字段让JSON保留子类信息,再自定义TypeAdapter根据标识字段创建对应子类实例。
步骤1:修改POJO类,添加类型标识
import lombok.AllArgsConstructor; import lombok.Data; class Mother { private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } } @Data @AllArgsConstructor class Son extends Mother { private int age; public Son(int age) { this.age = age; setType("son"); } } @Data @AllArgsConstructor class Daughter extends Mother { private int age; public Daughter(int age) { this.age = age; setType("daughter"); } }
步骤2:自定义Mother的TypeAdapter
import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; public class MotherTypeAdapter extends TypeAdapter<Mother> { @Override public void write(JsonWriter out, Mother value) throws IOException { out.beginObject(); out.name("type").value(value.getType()); if (value instanceof Son) { out.name("age").value(((Son) value).getAge()); } else if (value instanceof Daughter) { out.name("age").value(((Daughter) value).getAge()); } out.endObject(); } @Override public Mother read(JsonReader in) throws IOException { in.beginObject(); String type = null; int age = 0; while (in.hasNext()) { switch (in.nextName()) { case "type": type = in.nextString(); break; case "age": age = in.nextInt(); break; } } in.endObject(); if ("son".equals(type)) { return new Son(age); } else if ("daughter".equals(type)) { return new Daughter(age); } return null; } }
步骤3:修改测试代码,使用自定义TypeAdapter
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Test { public static void main(String[] args) { List<Son> sons = new ArrayList<>(); sons.add(new Son(12)); sons.add(new Son(13)); List<Daughter> daughters = new ArrayList<>(); daughters.add(new Daughter(5)); daughters.add(new Daughter(6)); Map<String, List<? extends Mother>> map = new HashMap<>(); map.put("ex-husband", sons); map.put("husband", daughters); Gson gson = new GsonBuilder() .registerTypeAdapter(Mother.class, new MotherTypeAdapter()) .create(); String json = gson.toJson(map); System.out.println(json); Type type = new TypeToken<Map<String, List<? extends Mother>>>() {}.getType(); Map<String, List<? extends Mother>> map1 = gson.fromJson(json, type); List<Son> sons1 = (List<Son>) map1.get("ex-husband"); System.out.println(sons1.get(0).getAge()); // 正常输出12 } }
解决方案2:使用Gson的RuntimeTypeAdapterFactory(推荐)
Gson扩展库gson-extras提供的RuntimeTypeAdapterFactory可简洁处理多态类型序列化/反序列化,无需手动添加类型字段。
步骤1:引入依赖(Maven示例)
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson-extras</artifactId> <version>2.10.1</version> <!-- 使用最新稳定版本 --> </dependency>
步骤2:修改测试代码,使用RuntimeTypeAdapterFactory
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.google.gson.typeadapters.RuntimeTypeAdapterFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Test { public static void main(String[] args) { RuntimeTypeAdapterFactory<Mother> typeFactory = RuntimeTypeAdapterFactory.of(Mother.class, "type") .registerSubtype(Son.class, "son") .registerSubtype(Daughter.class, "daughter"); List<Son> sons = new ArrayList<>(); sons.add(new Son(12)); sons.add(new Son(13)); List<Daughter> daughters = new ArrayList<>(); daughters.add(new Daughter(5)); daughters.add(new Daughter(6)); Map<String, List<? extends Mother>> map = new HashMap<>(); map.put("ex-husband", sons); map.put("husband", daughters); Gson gson = new GsonBuilder() .registerTypeAdapterFactory(typeFactory) .create(); String json = gson.toJson(map); System.out.println(json); Type type = new TypeToken<Map<String, List<? extends Mother>>>() {}.getType(); Map<String, List<? extends Mother>> map1 = gson.fromJson(json, type); List<Son> sons1 = (List<Son>) map1.get("ex-husband"); System.out.println(sons1.get(0).getAge()); // 正常输出12 } }
说明
RuntimeTypeAdapterFactory会自动在JSON中添加指定的类型字段,序列化时写入子类标识,反序列化时根据标识创建对应子类实例,无需手动处理JSON读写逻辑。
内容的提问来源于stack exchange,提问作者Ah Chong
相关产品推荐
相关产品推荐

