
本文详解 moshi 的 polymorphicjsonadapterfactory 在处理 `map
在使用 Moshi 进行多态 JSON 反序列化时,PolymorphicJsonAdapterFactory 能够根据 JSON 中的 type 字段(如 "type": "User")自动将数据映射为对应子类实例。但当目标类型涉及嵌套泛型结构(例如 Map<String, List<? extends Animal>>)时,若未正确声明完整的类型信息,Moshi 将无法对内层集合(如 List)中的元素应用多态适配器,导致反序列化结果为原始 LinkedTreeMap 或 LinkedHashMap —— 即“类型擦除后”的中间表示,而非预期的具体子类对象。
根本原因在于:PolymorphicJsonAdapterFactory 仅作用于其注册的基类(如 Animal.class)及其直接使用场景。当该基类出现在嵌套泛型中(如 List<Animal>),Moshi 需要显式获知完整、精确的参数化类型,才能将多态逻辑递归应用于集合内部元素。而错误写法:
// ❌ 错误:将 List.class 和 baseType 并列作为 Map 的类型参数 ParameterizedType type = Types.newParameterizedType(Map.class, String.class, List.class, baseType);
等价于 Map<String, List, Animal>(非法 Java 类型),实际被解析为 Map<String, List>,其中 List 的元素类型丢失,Moshi 无法识别需对 List 内部元素启用多态适配。
✅ 正确做法是使用 Types.newParameterizedType() 逐层构建嵌套类型,确保 List<? extends T> 本身作为一个完整的 ParameterizedType 传入 Map:
private static <T> Map<String, List<? extends T>> deserializeNestedPolymorphic(
Class<T> baseType,
List<Class<? extends T>> subclasses,
String json) {
// 构建多态工厂(支持 Animal → Dog/Cat)
PolymorphicJsonAdapterFactory<T> factory =
PolymorphicJsonAdapterFactory.of(baseType, "type");
for (Class<? extends T> subclass : subclasses) {
factory = factory.withSubtype(subclass, subclass.getSimpleName());
}
// ✅ 关键修正:正确构造嵌套类型 Map<String, List<T>>
ParameterizedType listType = Types.newParameterizedType(List.class, baseType);
ParameterizedType mapType = Types.newParameterizedType(Map.class, String.class, listType);
Moshi moshi = new Moshi.Builder().add(factory).build();
JsonAdapter<Map<String, List<? extends T>>> adapter = moshi.adapter(mapType);
return adapter.fromJson(json);
}? 提示:Types 是 Moshi 自带的 com.squareup.moshi.Types 工具类,专用于运行时泛型类型构造,不可替换为 java.lang.reflect.Type 的其他实现。
此外,还需确保 JSON 结构与类型定义严格匹配。例如,针对 Map<String, List<? extends Animal>>,合法 JSON 应形如:
{
"pets": [
{ "type": "Dog", "name": "Buddy", "barkVolume": 8 },
{ "type": "Cat", "name": "Luna", "livesLeft": 7 }
],
"zoo": [
{ "type": "Dog", "name": "Rex", "barkVolume": 9 }
]
}此时调用上述方法将返回 Map,其 value 是 List<Animal>,且每个元素均为 Dog 或 Cat 实例,而非 LinkedTreeMap。
总结:Moshi 多态反序列化在嵌套场景下失效,几乎总是源于 ParameterizedType 构造不完整。牢记原则——每一层泛型都必须用 Types.newParameterizedType() 显式封装,从最内层(如 List<Animal>)开始,逐级向外构建(如 Map<String, List<Animal>>)。这是解锁复杂领域模型 JSON 绑定的关键实践。


















