java泛型运行时类型擦除导致json序列化失败,jackson用typereference或javatype,gson用typetoken,均依赖匿名子类固化泛型签名。

泛型字段的 JSON 序列化难点,本质是 Java 运行时“看不见”泛型类型——比如 List<user></user> 在 JVM 里只剩 List,User 这个关键信息被擦除了。Jackson 和 Gson 都不能靠 List.class 猜出你要的是 User 还是 Order。它们的解法思路一致:绕过擦除,把泛型签名“固化”下来;但实现路径和使用细节有明显差异。
为什么直接传 List.class 会失败
Java 编译后,泛型只保留在字节码的 Signature 属性中,运行时 Class 对象不包含具体类型参数。所以:
-
gson.fromJson(json, List.class)→ 解析成List<linkedtreemap></linkedtreemap>(Gson 默认用 Map 表示未知对象) -
mapper.readValue(json, List.class)→ 反序列化为List<object></object>或抛ClassCastException - 哪怕你声明了
List<user> list</user>,变量声明本身不参与反序列化过程
Jackson 的两种可靠写法
Jackson 要求你提供能携带泛型信息的类型描述符,核心是利用匿名子类保留编译期泛型签名:
-
TypeReference:最常用,适合已知具体泛型的场景
objectMapper.readValue(json, new TypeReference<list>>() {});</list>
注意末尾的{}——必须是匿名子类,预定义子类无效 -
JavaType:适合动态构建类型,比如泛型类型来自参数
JavaType type = mapper.getTypeFactory().constructParametricType(List.class, User.class);<br> objectMapper.readValue(json, type);
Gson 的 TypeToken 是标准答案
Gson 同样依赖匿名子类机制,通过 TypeToken 提取泛型元数据:
- 基础用法:
TypeToken<list>> token = new TypeToken<list>>() {};<br> List<user> list = gson.fromJson(json, token.getType());</user></list></list> - 可复用:token 实例可缓存,
getType()返回的Type对象线程安全 - 不支持预定义子类:写成
class MyToken extends TypeToken<list>> {}</list>会失效,因为泛型信息不在其父类签名中
嵌套泛型与边界情况处理
复杂结构如 Map<string list>></string> 或带通配符的 List extends Person> 同样适用上述模式:
- Jackson:
new TypeReference<map list>>>() {}</map>
或用constructMapType+constructCollectionType组合构建 - Gson:
new TypeToken<map list>>>() {}.getType()</map> - 注意:含通配符或类型变量(如
T)的泛型,需确保在编译期能推导出具体类型,否则仍会丢失











