Jackson如何根据两个字段值将JSON反序列化为指定类
解决方案
你嵌套配置@JsonTypeInfo失效的核心原因是:Jackson默认的多态反序列化机制只会在当前层级解析一次类型标识字段,不会自动递归对子类做二次类型鉴别;同时你给Type1Abstract的anotherType字段加了@NotEmpty校验,但无another_type字段的响应本身就缺失该字段,自然无法完成实例化。
以下两种方案都可以实现双字段联合判定反序列化类型,比你当前用Any接收后手动强转的临时方案更健壮:
方案1:自定义类型ID解析器(推荐,完全符合Jackson标准流程)
不需要修改你现有的类抽象设计,通过扩展Jackson的类型解析逻辑实现双字段联合鉴别:
- 移除
Type1Abstract类上单独配置的@JsonTypeInfo和@JsonSubTypes注解,将所有分支类型(包括type=1下的3种场景)统一注册到顶层Response类的子类型列表中。 - 自定义
TypeIdResolver,将type和another_type两个字段的值拼接为唯一类型标识,示例实现逻辑:
class JointTypeIdResolver : TypeIdResolverBase() { override fun idFromValue(value: Any): String { return when (value) { is Type0 -> "0" is Type2 -> "2" is Type1Plain -> "1:" // 无another_type的场景 is Type1Fees -> "1:FEES" is Type1Other -> "1:OTHER" else -> throw IllegalArgumentException("Unsupported response type") } } override fun typeFromId(context: DatabindContext?, id: String?): JavaType? { return when (id) { "0" -> context?.constructType(Type0::class.java) "2" -> context?.constructType(Type2::class.java) "1:" -> context?.constructType(Type1Plain::class.java) "1:FEES" -> context?.constructType(Type1Fees::class.java) "1:OTHER" -> context?.constructType(Type1Other::class.java) else -> throw IllegalArgumentException("Unknown type identifier: $id") } } override fun getMechanism(): JsonTypeInfo.Id = JsonTypeInfo.Id.CUSTOM }
- 配套实现自定义的
TypeResolverBuilder,在解析类型ID阶段先读取完整的JSON节点,提取type字段值,当type=1时再读取another_type字段(缺失则默认取空串),两个字段拼接后传入上面的解析器匹配具体子类型。 - 将自定义解析器配置到顶层
Response的@JsonTypeInfo注解中即可。
该方案完全走Jackson原生的反序列化流程,所有字段校验、类型转换、注解配置都能正常生效,不需要手动处理Map强转逻辑。
方案2:默认实现+二次转换(实现成本更低)
如果不想编写自定义解析器,可以调整现有注解配置:
- 给
Type1Abstract的@JsonTypeInfo配置defaultImpl参数,指定一个中间过渡类,这个类只保存原始JsonNode格式的amount字段和another_type字段值,不需要定义具体的amount结构。 - 完成顶层反序列化后,判断如果当前响应是type=1类型,再根据
another_type的取值,调用ObjectMapper将过渡类中保存的JsonNode转换为对应的Type1Plain、Type1Fees、Type1Other具体实例。
针对你当前临时方案的优化建议
你目前用Any接收amount的实现存在边界风险:当amount是整数值时,Jackson默认会解析为Int类型而非Double,容易触发类型转换异常;同时直接强转LinkedHashMap取值时没有空值和类型校验,接口返回结构变动时很难排查问题。如果暂时不重构为上述规范方案,建议将amount的接收类型改为JsonNode,通过Jackson内置的节点API做类型判断和取值,比直接用Any安全很多。
内容的提问来源于stack exchange,提问作者Juan Ignacio Noli Villar
相关产品推荐
相关产品推荐

