
本文详解如何在 Jackson 中正确使用 @JsonTypeInfo 和 @JsonSubTypes 实现基于 method 字段的多态反序列化,尤其解决泛型集合(如 List)无法直接标注类型信息导致的 MismatchedInputException 问题。
本文详解如何在 jackson 中正确使用 `@jsontypeinfo` 和 `@jsonsubtypes` 实现基于 `method` 字段的多态反序列化,尤其解决泛型集合(如 `list
在 Jackson 的多态反序列化场景中,一个常见误区是试图将 @JsonTypeInfo 和 @JsonSubTypes 直接应用于泛型集合字段(例如 List
com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information...
该错误的根本原因在于:Jackson 不支持对参数化集合类型(如 List
✅ 正确解法是:将多态逻辑下沉到封装类层级,而非泛型字段层级。即,为每种 method 值定义一个明确的、非泛型的响应包装类,每个类内部持有对应类型的 List
✅ 推荐结构:为每种 method 定义专用包装类
// 1. 公共基类或标记接口(可选,增强类型安全)
public interface ResponseData {}
// 2. 具体数据项类型
public record AccountLedgerResponse(
String userid,
String datestamp,
String orderid,
String accountname,
String messageid,
String transactiontype,
String currency,
String amount,
String gluepayid
) implements ResponseData {}
// 3. 专用于 "AccountLedger" method 的响应包装类(关键!)
public record AccountLedgerResult(
String signature,
UUID uuid,
Method method, // 可设为 enum
List<AccountLedgerResponse> data
) implements ResultWrapper {}
// 4. 其他 method 同理,例如:
public record TradeHistoryResult(
String signature,
UUID uuid,
Method method,
List<TradeRecord> data
) implements ResultWrapper {}✅ 配置顶层多态解析器(在根 Result 包装类上)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "method"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = AccountLedgerResult.class, name = "AccountLedger"),
@JsonSubTypes.Type(value = TradeHistoryResult.class, name = "TradeHistory"),
// ... 其他 method 映射
})
public sealed interface ResultWrapper permits AccountLedgerResult, TradeHistoryResult {
String signature();
UUID uuid();
Method method();
}再定义顶层响应结构(不含泛型):
public record ApiResponse(
String version,
@JsonUnwrapped(prefix = "") // 或直接嵌套
ResultWrapper result
) {}✅ 反序列化示例
ObjectMapper mapper = new ObjectMapper();
ApiResponse response = mapper.readValue(jsonString, ApiResponse.class);
if (response.result() instanceof AccountLedgerResult ledger) {
System.out.println("Parsed " + ledger.data().size() + " ledger entries");
ledger.data().forEach(entry ->
System.out.println("Account: " + entry.accountname())
);
}⚠️ 注意事项与最佳实践
-
不要在泛型字段上使用 @JsonTypeInfo:List
、Optional 等类型无法参与 EXTERNAL_PROPERTY 多态,Jackson 无法推导其运行时具体类型。 - As.WRAPPER_ARRAY 仅适用于数组包装场景(如 {"type":"A","value":[...]}),本例中 JSON 是扁平结构("data" 直接为数组),故必须用 As.EXTERNAL_PROPERTY + 封装类。
- 确保 name 值严格匹配 JSON 中的 "method" 字符串(大小写敏感),建议使用 enum Method 并通过 @JsonValue 控制序列化值。
- 若需保留泛型灵活性,可在业务层做适配:将 ResultWrapper 转换为 ResultList
,但反序列化阶段必须绕过泛型擦除限制。
通过将多态责任交给具体、非泛型的包装类,你既能保持 JSON 结构不变,又能获得类型安全、可维护的反序列化逻辑——这是 Jackson 处理动态 API 响应的最佳实践之一。

















