如何解决Kotlin密封类Jackson自定义反序列化的栈溢出问题?
Kotlin密封类Jackson自定义反序列化器避免递归栈溢出的正确实现
你的问题核心是在自定义反序列化器中直接尝试反序列化ParentClass类型,触发了注解绑定的反序列化器,导致无限递归。以下是正确的实现方案:
1. 修正密封类注解
首先给密封类添加子类声明注解,让Jackson识别所有可能的子类:
@JsonDeserialize(using = ParentClassDeserializer::class) @JsonSubTypes( JsonSubTypes.Type(value = ParentClass.Class1::class, name = "class1"), JsonSubTypes.Type(value = ParentClass.Class2::class, name = "class2") ) sealed class ParentClass { // 建议用data class方便Jackson序列化/反序列化 data class Class1(val field1: String? = null) : ParentClass() data class Class2(val field2: Int? = null) : ParentClass() }
2. 实现无递归的自定义反序列化器
在反序列化器中,直接针对具体子类进行反序列化,绕过ParentClass的自定义反序列化逻辑:
class ParentClassDeserializer : JsonDeserializer<ParentClass>() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): ParentClass { val node = p.codec.readTree(p) // 假设JSON中存在"type"字段区分子类,也可通过字段存在性判断 val type = node.get("type").asText() return when (type) { "class1" -> ctxt.readValue( node.traverse(ctxt.parserFactory), ParentClass.Class1::class.java ) "class2" -> ctxt.readValue( node.traverse(ctxt.parserFactory), ParentClass.Class2::class.java ) else -> throw IllegalArgumentException("未知类型: $type") } } }
替代判断逻辑(无type字段时)
如果JSON中没有显式的类型标识,可以通过子类特有的字段判断:
class ParentClassDeserializer : JsonDeserializer<ParentClass>() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): ParentClass { val node = p.codec.readTree(p) return when { node.has("field1") -> p.codec.treeToValue(node, ParentClass.Class1::class.java) node.has("field2") -> p.codec.treeToValue(node, ParentClass.Class2::class.java) else -> throw ctxt.mappingException("无法匹配ParentClass的任何子类") } } }
3. 必要的依赖与配置
确保引入Jackson Kotlin模块,让Jackson更好支持Kotlin特性:
// Gradle依赖 implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2")
初始化ObjectMapper时注册Kotlin模块:
val objectMapper = ObjectMapper().registerModule(KotlinModule())
关键原理说明
你之前的代码触发递归的原因是:调用mapper.readTree(node, ParentClass::class.java)时,Jackson会读取ParentClass上的@JsonDeserialize注解,再次调用你的自定义反序列化器,形成无限循环。而直接反序列化到具体子类时,Jackson会使用默认的反序列化逻辑,不会触发ParentClass的自定义反序列化器,从而避免递归。
内容的提问来源于stack exchange,提问作者Mohammad Salik
相关产品推荐
相关产品推荐

