Spring Boot中Jackson空键序列化配置无效,报keySerializer为null错误
问题描述
序列化包含空键的Map对象时触发以下错误:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Cannot invoke "com.fasterxml.jackson.databind.JsonSerializer.serialize(Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)" because "keySerializer" is null]
尝试注册Jackson自定义配置类(如下)但未生效:
@Bean fun jsonCustomizer(): Jackson2ObjectMapperBuilderCustomizer = Jackson2ObjectMapperBuilderCustomizer { builder: Jackson2ObjectMapperBuilder -> builder.postConfigurer { objectMapper: ObjectMapper -> objectMapper .serializerProvider .setNullKeySerializer( object : JsonSerializer<Any>() { override fun serialize( value: Any, gen: JsonGenerator, serializers: SerializerProvider, ) { gen.writeFieldName("") } }, ) } }
补充详细错误栈:
com.fasterxml.jackson.databind.JsonMappingException: Cannot invoke "com.fasterxml.jackson.databind.JsonSerializer.serialize(Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)" because "keySerializer" is null (引用链:java.util.ArrayList[0]->com.example.MyOuterFoo["outerFooKey"]->com.example.SecondFoo["secondFooKey"]->java.util.Arrays$ArrayList[0]->com.example.InnerFoo["innerFooKey"]->java.util.LinkedHashMap["hashMapKey"])
问题原因
直接修改objectMapper.serializerProvider的nullKeySerializer不生效,因为Jackson的SerializerProvider实例通常是不可变的,修改现有实例不会影响后续序列化使用的Provider对象。
解决方案
方案1:创建新的SerializerProvider实例替换原有对象
通过创建新的SerializerProvider实例并设置到ObjectMapper中,确保空键序列化配置生效:
@Bean fun jsonCustomizer(): Jackson2ObjectMapperBuilderCustomizer = Jackson2ObjectMapperBuilderCustomizer { builder -> builder.postConfigurer { objectMapper -> // 创建新的SerializerProvider实例 val serializerProvider = objectMapper.serializerProvider .createInstance(objectMapper.serializationConfig, objectMapper.serializerFactory) // 设置空键序列化器 serializerProvider.setNullKeySerializer(object : JsonSerializer<Any>() { override fun serialize(value: Any, gen: JsonGenerator, serializers: SerializerProvider) { // 将空键序列化为空字符串 gen.writeFieldName("") } }) // 替换ObjectMapper的SerializerProvider objectMapper.setSerializerProvider(serializerProvider) } }
方案2:通过SimpleModule注册全局键序列化器
注册一个自定义的键序列化器,统一处理空键和常规键的序列化:
@Bean fun jsonCustomizer(): Jackson2ObjectMapperBuilderCustomizer = Jackson2ObjectMapperBuilderCustomizer { builder -> builder.modules(object : SimpleModule() { init { addKeySerializer(Any::class.java, object : JsonSerializer<Any>() { override fun serialize(value: Any?, gen: JsonGenerator, serializers: SerializerProvider) { if (value == null) { // 空键序列化为空字符串 gen.writeFieldName("") } else { // 非空键使用默认序列化逻辑 serializers.defaultSerializeKey(value, gen) } } }) } }) }
验证说明
两种方案都能确保Jackson在序列化含空键的Map时,不会出现keySerializer为null的错误,同时将空键序列化为指定的空字符串(可根据需求修改writeFieldName的参数)。
内容的提问来源于stack exchange,提问作者pixel

