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

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:13:11