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

Spring Boot3.2+Java21升级后Jackson自定义类型解析序列化报错

Spring Boot 3.2 + Java 21 + Jackson 2.15.3 序列化异常排查思路

问题背景

升级Spring Boot至3.2、Java至21,同步更新Jackson到2.15.3后,实现接口且配置JsonTypeIdResolver的类出现序列化异常。执行ObjectMapperService.objectMapperForFilters.writeValueAsString(Screen1Filter(...))时抛出:

com.fasterxml.jackson.core.JsonGenerationException: Can not write a field name, expecting a value

未配置JsonTypeIdResolver的对象序列化正常。

相关代码

接口定义

@JsonTypeInfo(
    use = JsonTypeInfo.Id.CUSTOM,
    include = JsonTypeInfo.As.EXISTING_PROPERTY,
    property = "screen",
    visible = true
)
@JsonTypeIdResolver(BaseFilterResolver::class)
interface BaseFilter: ProjectFilter {
    val id: Long?
    val screen: Screen
}

实现类

data class Screen1Filter(
        override val id: Long? = null,
        val period: Period?,
        //... other properties
        override val screen: Screen = Screen.SCREEN1
): BaseFilter {...}

自定义类型ID解析器

class BaseFilterResolver : TypeIdResolverBase() {
    private var superType: JavaType? = null
    override fun init(baseType: JavaType?) {
        superType = baseType
    }

    override fun idFromValue(obj: Any): String? {
        return null
    }

    override fun idFromValueAndType(obj: Any?, subType: Class<*>): String? {
        return null
    }

    override fun typeFromId(context: DatabindContext, id: String?): JavaType {
        return context.constructSpecializedType(superType, Screen.values().find { it.name == id }!!.filterClass)
    }

    override fun getMechanism(): JsonTypeInfo.Id {
        return JsonTypeInfo.Id.CUSTOM;
    }
}

解决思路

  • 修复ID生成逻辑:你的BaseFilterResolver中idFromValue和idFromValueAndType方法都返回null,Jackson序列化时依赖这两个方法获取类型标识,返回null会打乱序列化流程,触发字段写入顺序错误。修改这两个方法,从对象中提取screen字段的名称作为ID:
    override fun idFromValue(obj: Any): String? {
        return (obj as BaseFilter).screen.name
    }
    
    override fun idFromValueAndType(obj: Any?, subType: Class<*>): String? {
        return obj?.let { (it as BaseFilter).screen.name } 
            ?: Screen.values().find { it.filterClass == subType }?.name
    }
    
  • 匹配配置与Resolver逻辑:Jackson 2.15.x对自定义类型解析器的校验更严格,你使用include = As.EXISTING_PROPERTY意味着复用已有screen字段作为类型标识,Resolver必须正确返回该字段对应的序列化值,不能返回null。
  • 验证枚举映射一致性:确认Screen枚举的name属性和screen字段序列化后的输出值完全匹配,避免反序列化时类型匹配失败。
  • 开启调试日志:将com.fasterxml.jackson.databind日志级别设为DEBUG,观察类型ID的生成和写入过程,确认Resolver输出符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 08:00:17