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

Spring Boot Kotlin密封类Exactly类型Jackson反序列化失败求助

Kotlin密封类单参数Data Class Jackson反序列化失败问题解决

问题场景

在Spring Boot结合Kotlin开发时,密封类NumberRange的Range类型反序列化正常,但Exactly类型反序列化失败,序列化功能则完全正常。

相关代码:

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.WRAPPER_OBJECT,
    property = "type"
)
sealed class NumberRange {
    @JsonIgnore
    abstract fun isEmpty(): Boolean

    @JsonIgnore
    abstract fun rangeSize(): UInt

    @get:JsonIgnore
    abstract val first: UInt

    @JsonTypeName("Exactly")
    data class Exactly(val size: UInt) : NumberRange() {
        override fun isEmpty(): Boolean = size == 0u
        override fun rangeSize(): UInt = if (isEmpty()) 0u else 1u
        override val first: UInt = size
    }

    @JsonTypeName("Range")
    data class Range(val start: UInt, val end: UInt) : NumberRange() {
        override fun isEmpty(): Boolean = end > start
        override fun rangeSize(): UInt = if (isEmpty()) 0u else end - start + 1u
        override val first: UInt = start
    }
}

报错信息:

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.optiwatch.apis.optimizations.entities.NumberRange$Exactly` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

特殊现象:给Exactly类添加一个带默认值的成员变量后,反序列化恢复正常:

data class Exactly(val size: UInt, val ignore: UInt = 0u) : NumberRange()

问题原因

这是Kotlin单参数data class与Jackson序列化逻辑的适配问题:

  • 当data class只有一个参数时,Kotlin编译后生成的构造函数和元数据,会让Jackson尝试使用**委托构造器(delegate creator)**进行反序列化
  • 但配合JsonTypeInfo.As.WRAPPER_OBJECT这种外层包裹类型信息的序列化方式时,Jackson的解析逻辑会出现冲突,无法正确识别单参数构造器作为属性构造器
  • 添加带默认值的参数后,Kotlin会生成多参数构造函数,Jackson能正确识别属性构造器,从而完成反序列化

解决方案

方案1:给单参数添加@JsonProperty注解

直接给Exactly的构造参数加上@JsonProperty,明确指定属性名称,让Jackson正确识别属性构造器:

@JsonTypeName("Exactly")
data class Exactly(@JsonProperty("size") val size: UInt) : NumberRange() {
    override fun isEmpty(): Boolean = size == 0u
    override fun rangeSize(): UInt = if (isEmpty()) 0u else 1u
    override val first: UInt = size
}

方案2:确保Jackson Kotlin模块正确配置

Spring Boot通常会自动引入jackson-module-kotlin,但如果有自定义ObjectMapper配置,需确保模块正确注册,该模块专门处理Kotlin的序列化/反序列化适配问题:

@Configuration
class JacksonConfig {
    @Bean
    fun objectMapper(): ObjectMapper {
        return ObjectMapper()
            .registerModule(KotlinModule.Builder().build())
    }
}

方案3:手动添加无参构造函数(不推荐)

通过手动添加无参构造函数,让Jackson能找到可用的构造器,但这种方式违背data class的设计初衷,仅作为临时应急方案:

@JsonTypeName("Exactly")
data class Exactly(val size: UInt) : NumberRange() {
    constructor() : this(0u) // 手动添加无参构造
    override fun isEmpty(): Boolean = size == 0u
    override fun rangeSize(): UInt = if (isEmpty()) 0u else 1u
    override val first: UInt = size
}

推荐方案

优先选择方案1(针对性解决单参数问题)或方案2(全局适配Kotlin序列化场景),避免使用方案3。

内容的提问来源于stack exchange,提问作者Ayoub

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 21:45:30