使用@JsonTypeInfo和@JsonSubTypes反序列化枚举至接口时遇异常
Jackson枚举反序列化为接口时的InvalidTypeIdException问题解决
尝试使用Jackson将实现同一基础接口的多个枚举类反序列化时,通过@JsonTypeInfo和@JsonSubTypes指定枚举类型,却抛出InvalidTypeIdException异常。
代码示例
enum class FooEnum : InterfaceForEnum { BAR_ELEM, BAZ_ELEM } enum class MooEnum : InterfaceForEnum { CAR_ELEM, CAZ_ELEM } @JsonTypeInfo(include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "enumType", use = JsonTypeInfo.Id.NAME) @JsonSubTypes( JsonSubTypes.Type(name = "Foo", value = FooEnum::class), JsonSubTypes.Type(name = "Moo", value = MooEnum::class) ) interface InterfaceForEnum data class SomeClassWithInterfaceField(val enumInterfaceInstance: InterfaceForEnum) fun main() { val jsonFactory = JsonFactory.builder().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION).build() val mapper = ObjectMapper(jsonFactory).registerKotlinModule() val json = """ { "enumType": "Foo", "enumInterfaceInstance" : "BAR_ELEM" } """ mapper.readValue(json, SomeClassWithInterfaceField::class.java) }
异常信息
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class InterfaceForEnum]: missing type id property 'enumType' (for POJO property 'enumInterfaceInstance') at [Source: (String)" { "enumType": "Foo", "enumInterfaceInstance" : "BAR_ELEM" } "; line: 4, column: 39] (through reference chain: SomeClassWithInterfaceField["enumInterfaceInstance"])
问题原因
Jackson对枚举类型有专门的反序列化逻辑,默认会直接将字符串匹配为枚举常量。当在接口上使用EXTERNAL_PROPERTY类型的@JsonTypeInfo时,Jackson无法将外部同级的enumType字段与枚举字符串值关联,导致无法识别要反序列化的具体枚举子类。
解决方案
方案1:调整JSON结构与注解配置
将枚举序列化为对象格式,把类型信息嵌入枚举对象中,修改注解和JSON结构:
- 修改接口的注解配置:
@JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, property = "@type", use = JsonTypeInfo.Id.NAME) @JsonSubTypes( JsonSubTypes.Type(name = "Foo", value = FooEnum::class), JsonSubTypes.Type(name = "Moo", value = MooEnum::class) ) interface InterfaceForEnum
- 给每个枚举类添加
@JsonFormat注解,指定序列化为对象:
@JsonFormat(shape = JsonFormat.Shape.OBJECT) enum class FooEnum : InterfaceForEnum { BAR_ELEM, BAZ_ELEM } @JsonFormat(shape = JsonFormat.Shape.OBJECT) enum class MooEnum : InterfaceForEnum { CAR_ELEM, CAZ_ELEM }
- 修改JSON结构为:
{ "enumInterfaceInstance": { "@type": "Foo", "name": "BAR_ELEM" } }
方案2:保留原JSON结构,手动处理反序列化
通过@JsonCreator自定义构造逻辑,根据enumType字段选择对应的枚举类:
data class SomeClassWithInterfaceField( val enumType: String, val enumInterfaceInstance: InterfaceForEnum ) { companion object { @JsonCreator @JvmStatic fun create( @JsonProperty("enumType") enumType: String, @JsonProperty("enumInterfaceInstance") value: String ): SomeClassWithInterfaceField { val enumInstance = when (enumType) { "Foo" -> FooEnum.valueOf(value) "Moo" -> MooEnum.valueOf(value) else -> throw IllegalArgumentException("未知枚举类型: $enumType") } return SomeClassWithInterfaceField(enumType, enumInstance) } } }
这样即可直接处理原JSON结构,无需修改接口和枚举类的注解。
内容的提问来源于stack exchange,提问作者Alex78191
相关产品推荐
相关产品推荐

