
本文详解如何在 Jackson 中结合 @JsonTypeInfo 和 @JsonSubTypes 正确处理「基于字段值动态选择泛型集合类型」的反序列化场景,解决 MismatchedInputException: expected START_ARRAY 等常见错误。
本文详解如何在 jackson 中结合 `@jsontypeinfo` 和 `@jsonsubtypes` 正确处理「基于字段值动态选择泛型集合类型」的反序列化场景,解决 `mismatchedinputexception: expected start_array` 等常见错误。
在使用 Jackson 进行多态反序列化(polymorphic deserialization)时,一个典型难点是:当 JSON 中某个字段(如 "method": "AccountLedger")决定了另一字段(如 "data")的实际 Java 类型,且该字段本身是泛型集合(如 List
核心问题在于:@JsonTypeInfo 不能直接作用于 List
✅ 正确做法如下:
-
定义具体响应包装类(而非仅泛型记录):
public class AccountLedgerList { @JsonProperty("data") private List<AccountLedgerResponse> data; // 必须提供无参构造器(Jackson 反序列化所需) public AccountLedgerList() {} public AccountLedgerList(List<AccountLedgerResponse> data) { this.data = data; } public List<AccountLedgerResponse> getData() { return data; } } -
在顶层结果类中,将 @JsonTypeInfo + @JsonSubTypes 应用于该包装类字段:
public record ResultWrapper( @JsonProperty("version") String version, @JsonProperty("result") @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "method" // 从 result 对象内部读取 method 字段 ) @JsonSubTypes({ @JsonSubTypes.Type(value = AccountLedgerList.class, name = "AccountLedger"), // 可扩展其他 method → 类型映射,如: // @JsonSubTypes.Type(value = TradeHistoryList.class, name = "TradeHistory") }) Object result // 注意:此处类型为 Object 或统一基类(如 ResponseListBase) ) {}
⚠️ 关键注意事项:
- include = JsonTypeInfo.As.EXTERNAL_PROPERTY 表示类型标识来自同一层级的另一个字段(即 result.method),因此 @JsonTypeInfo 必须标注在 result 字段上,而非其内部字段;
- result 字段类型不能是泛型记录(如 ResultList
),因为泛型擦除后 Jackson 无法确定子类型边界;建议使用非泛型基类或 Object,再通过 @JsonSubTypes 显式声明所有可能的实现类; - 每个子类型(如 AccountLedgerList)必须有默认构造器,并确保其内部 data 字段能被 Jackson 正确映射(添加 @JsonProperty("data") 更稳妥);
- 若需复用逻辑,可定义抽象基类 ResponseListBase,让 AccountLedgerList 等继承它,然后将 @JsonSubTypes 指向该基类。
? 进阶提示:若希望保留泛型抽象能力,可配合 TypeReference 手动反序列化:
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
String method = root.path("result").path("method").asText();
JavaType type = switch (method) {
case "AccountLedger" -> mapper.getTypeFactory()
.constructParametricType(AccountLedgerList.class, AccountLedgerResponse.class);
default -> throw new IllegalArgumentException("Unknown method: " + method);
};
AccountLedgerList list = mapper.treeToValue(root.path("result"), type);总结:Jackson 的多态机制依赖具体类的显式注册,而非泛型占位符。面对 "method" 驱动的 data 数组类型变化,务必为每种 method 值设计对应的、含 List

















