Jackson 如何在缺少 type 字段时显式反序列化子类?
可以。对于已明确知道目标类型的旧数据,可以使用独立的 ObjectMapper,通过 Mixin 为 DataProcessingJob 设置 @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)。这条读取路径不再要求 JSON 提供 type,原有 mapper 仍可按接口上的配置处理多态数据。
为什么指定了具体类,仍然要求 type?
JobDefinition 上的 @JsonTypeInfo 会影响实现类的类型处理。调用:
OBJECT_MAPPER.readValue(jsonBlob, DataProcessingJob.class);
指定 DataProcessingJob.class 不会自动取消适用于该类的多态配置,Jackson 仍会尝试读取 type。因此,缺少 type 的旧 JSON 会触发 InvalidTypeIdException。
FAIL_ON_UNKNOWN_PROPERTIES 控制如何处理 JSON 中无法映射的普通属性,与缺少多态类型标识无关。将其设为 false 不能解决这个异常。
用 Mixin 为旧数据关闭类型标识处理
先定义一个 Mixin。它不需要实现 JobDefinition,只负责提供覆盖注解:
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
abstract class NoTypeInfoMixin {
}
然后复制现有 mapper,并将 Mixin 注册到具体实现类:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper legacyMapper = OBJECT_MAPPER.copy();
legacyMapper.addMixIn(DataProcessingJob.class, NoTypeInfoMixin.class);
DataProcessingJob job =
legacyMapper.readValue(jsonBlob, DataProcessingJob.class);
这里沿用问题中的 OBJECT_MAPPER 和 jsonBlob。copy() 保留现有 mapper 的配置,注册的 Mixin 会覆盖副本中 DataProcessingJob 的类型信息设置。
Jackson 会将 DataProcessingJob 作为普通对象反序列化,无需从 JSON 中解析 type。其他字段仍须满足 DataProcessingJob 本身的反序列化要求。
保留接口的多态读取能力
包含类型标识的新数据继续使用原有 mapper:
JobDefinition job =
OBJECT_MAPPER.readValue(jsonBlob, JobDefinition.class);
已知具体类型的旧数据使用 legacyMapper:
DataProcessingJob job =
legacyMapper.readValue(jsonBlob, DataProcessingJob.class);
两个 mapper 的配置彼此独立,无需修改接口或实现类上的现有注解。
使用时需要注意什么?
- 在首次读写前完成 Mixin 注册,之后复用配置好的
legacyMapper,避免每读取一条记录都复制 mapper。 - 仅在业务已确定目标类型时使用这条旧数据读取路径。如果 JSON 缺少
type,调用方也不知道具体实现类,这个方案无法判断目标类型。 - 不要在仍负责正常多态读写的共享 mapper 上直接注册这个 Mixin,否则会改变该 mapper 对
DataProcessingJob的处理方式。 Id.NONE会影响该类型的类型信息处理。如果还需要输出带type的新数据,应继续使用原有 mapper 进行序列化。
备注:内容仅供参考。
