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

使用Jackson将JsonNode转特定类实例时出现StackOverflowError

解决Jackson自定义反序列化器循环调用导致的StackOverflowError问题

问题场景

编写JSON入站数据包反序列化器时,通过post_type和meta_event_type字段区分不同数据包类型,对应实现了密封类层级结构:EventData -> MetaEventData -> LifecycleMetaEventData/HeartbeatEventData,并为父类添加自定义反序列化器动态选择子类。

但反序列化时触发StackOverflowError,原因是:在MetaEventDataDeserializer中调用node.deserializeTo<LifecycleMetaEventData>(mapper)时,由于LifecycleMetaEventData继承自MetaEventData,而MetaEventData标注了@JsonDeserialize(using = MetaEventDataDeserializer::class),Jackson会再次触发MetaEventDataDeserializer,形成无限循环调用。

优雅解决方案

方案1:利用Jackson原生多态反序列化(推荐)

直接使用Jackson的多态反序列化特性替代自定义反序列化器,代码更简洁且原生支持类型判断:

  1. 为EventData添加多态配置:
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "post_type" // 用于区分类型的字段
)
@JsonSubTypes(
    JsonSubTypes.Type(value = MetaEventData::class, name = META_EVENT),
    // 可添加其他post_type对应的子类,比如MessageEventData::class
)
sealed class EventData {
    abstract val postType: String
}
  1. 为MetaEventData添加多态配置:
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "meta_event_type" // 用于区分MetaEvent子类的字段
)
@JsonSubTypes(
    JsonSubTypes.Type(value = LifecycleMetaEventData::class, name = LIFECYCLE),
    JsonSubTypes.Type(value = HeartbeatEventData::class, name = HEARTBEAT)
)
sealed class MetaEventData : EventData() {
    abstract val metaEventType: String
}
  1. 移除所有自定义反序列化器类,以及类上的@JsonDeserialize(using = ...)注解。

Jackson会自动根据配置的字段值选择对应子类完成反序列化,完全避免循环调用问题,代码维护成本更低。

方案2:临时覆盖父类反序列化器配置

如果无法使用多态反序列化,可以在反序列化子类时,临时创建移除了父类自定义反序列化器的ObjectMapper实例:

修改MetaEventDataDeserializer:

object MetaEventDataDeserializer : StdDeserializer<MetaEventData>(MetaEventData::class.java) {
    private fun readResolve(): Any = MetaEventDataDeserializer
    override fun deserialize(p: JsonParser, ctxt: DeserializationContext): MetaEventData {
        val node = p.readValueAsTree<ObjectNode>()
        val mapper = p.codec as ObjectMapper

        // 创建临时ObjectMapper,覆盖MetaEventData的反序列化器配置
        val tempMapper = mapper.copy().apply {
            configOverride(MetaEventData::class.java)
                .setDeserializerOverride(BeanDeserializerModifier.None)
        }

        return when (val metaEventType = node.getNotNull(META_EVENT_TYPE).asText()) {
            LIFECYCLE -> node.deserializeTo<LifecycleMetaEventData>(tempMapper)
            HEARTBEAT -> node.deserializeTo<HeartbeatEventData>(tempMapper)
            else -> throw IllegalArgumentException("Unexpected meta event type: $metaEventType")
        }
    }
}

通过临时禁用MetaEventData的自定义反序列化器,Jackson会使用默认Bean反序列化器处理子类,避免循环调用。

方案3:直接解析为子类类型

使用treeToValue方法直接将节点转换为指定子类,绕过父类的自定义反序列化器:

修改MetaEventDataDeserializer:

object MetaEventDataDeserializer : StdDeserializer<MetaEventData>(MetaEventData::class.java) {
    private fun readResolve(): Any = MetaEventDataDeserializer
    override fun deserialize(p: JsonParser, ctxt: DeserializationContext): MetaEventData {
        val node = p.readValueAsTree<ObjectNode>()
        val mapper = p.codec as ObjectMapper

        return when (val metaEventType = node.getNotNull(META_EVENT_TYPE).asText()) {
            LIFECYCLE -> mapper.treeToValue(node, LifecycleMetaEventData::class.java)
            HEARTBEAT -> mapper.treeToValue(node, HeartbeatEventData::class.java)
            else -> throw IllegalArgumentException("Unexpected meta event type: $metaEventType")
        }
    }
}

treeToValue会直接针对目标子类进行反序列化,不会触发父类的自定义反序列化逻辑,简单直接解决循环问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:34:57