You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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结构:

  1. 修改接口的注解配置:
@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
  1. 给每个枚举类添加@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 }
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:37:48