Spring Boot 3.0升级后Jackson反序列化空对象至Enum遇异常求助
问题:Spring Boot 3.0(Jackson 2.17)枚举反序列化空对象失败
将Spring Boot应用从2.7.x升级到3.0.x后,反序列化包含空对象{}的枚举集合字段时出现失败。同样的代码在Jackson 2.13中正常运行,但Jackson 2.17中抛出类型不匹配异常。需要实现一个支持任意枚举类的自定义反序列化器,同时满足以下约束:
- POJO类由第三方提供,无法修改JSON结构或在字段上添加
@JsonDeserialize注解 - 不能用正则替换JSON中的空对象,避免引入全局风险
- 不能为每个枚举类单独注册模块,要适配未来新增的枚举类
JSON示例
{ "lineItems":[ {}, {} ] }
实体类
@ToString @Data public class YourClass { private List<ItemEnum> lineItems; }
枚举类
public enum ItemEnum { ITEM_1, ITEM_2, ITEM_3 }
测试代码
public static void main(String[] args) throws JsonMappingException, JsonProcessingException { // 修正原测试代码中的JSON格式错误 String json = "{\"lineItems\":[{},{}]}"; ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL); objectMapper.coercionConfigFor(LogicalType.Array) .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull) .setCoercion(CoercionInputShape.EmptyObject, CoercionAction.AsNull) .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.AsNull); objectMapper.coercionConfigFor(LogicalType.Collection) .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull) .setCoercion(CoercionInputShape.EmptyObject, CoercionAction.AsNull) .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.AsNull); objectMapper.coercionConfigFor(LogicalType.Enum) .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull) .setCoercion(CoercionInputShape.EmptyObject, CoercionAction.AsNull) .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.AsNull); YourClass obj = objectMapper.readValue(json, YourClass.class); System.err.println(obj); }
错误信息
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `com.demo.ItemEnum` from Object value (token `JsonToken.START_OBJECT`) at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 5, column: 3] (through reference chain: com.demo.YourClass["lineItems"]->java.util.ArrayList[0]) at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1767) at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1541) at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1446) at com.fasterxml.jackson.databind.DeserializationContext.extractScalarFromObject(DeserializationContext.java:958) at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:290) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30) at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177) at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4905) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3848) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3816) at com.demo.Application.main(Application.java:57)
解决方案:通用枚举空对象反序列化器
1. 实现通用枚举反序列化器
继承Jackson原生的EnumDeserializer,重写deserialize方法,遇到空对象时直接返回null,其他场景保留原有枚举反序列化逻辑:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonToken; import com.fasterxml.jackson.databind.deser.std.EnumDeserializer; import com.fasterxml.jackson.databind.type.EnumType; import java.io.IOException; public class UniversalEnumEmptyObjectDeserializer extends EnumDeserializer { public UniversalEnumEmptyObjectDeserializer(EnumType enumType) { super(enumType); } @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 检测到空对象时返回null,并跳过整个空对象的token流 if (p.getCurrentToken() == JsonToken.START_OBJECT) { p.skipChildren(); return null; } // 正常枚举值反序列化逻辑 return super.deserialize(p, ctxt); } }
2. 注册全局反序列化模块
创建自定义模块,通过BeanDeserializerModifier为所有枚举类型统一替换反序列化器,无需逐个枚举注册:
import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.EnumType; public class EnumEmptyObjectModule extends SimpleModule { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.addBeanDeserializerModifier(new com.fasterxml.jackson.databind.deser.BeanDeserializerModifier() { @Override public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, EnumType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { // 替换为自定义的通用枚举反序列化器 return new UniversalEnumEmptyObjectDeserializer(type); } }); } }
3. 修改测试代码验证
在ObjectMapper中注册自定义模块,原有coercion配置可移除(自定义反序列化器已处理空对象场景):
public static void main(String[] args) throws JsonMappingException, JsonProcessingException { String json = "{\"lineItems\":[{},{}]}"; ObjectMapper objectMapper = new ObjectMapper(); // 保留未知枚举值转为null的配置 objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL); // 注册全局自定义模块 objectMapper.registerModule(new EnumEmptyObjectModule()); YourClass obj = objectMapper.readValue(json, YourClass.class); System.err.println(obj); // 输出:YourClass(lineItems=[null, null]) }
方案说明
- 完全满足约束:无需修改第三方POJO、无需正则修改JSON、全局适配所有现有及未来新增的枚举类
- 兼容性:仅拦截空对象场景,不影响正常枚举值的反序列化逻辑
- 稳定性:通过
skipChildren()跳过空对象的token流,避免后续解析出现异常
内容的提问来源于stack exchange,提问作者Swathi Ragupathi
相关产品推荐
相关产品推荐

