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
相关产品推荐
相关产品推荐

