Spring枚举MongoDB序列化异常:自定义值读写失败
这个问题我之前也踩过坑!核心原因是Spring Data MongoDB 并没有使用Jackson来处理枚举的序列化/反序列化——它自带了一套默认的枚举转换逻辑:存的时候直接写枚举的大写名称,读的时候调用Enum.valueOf()解析,所以你加的@JsonCreator、@JsonValue注解只在HTTP请求的@RequestBody处理中生效,对MongoDB的读写完全没影响。
下面给你两种靠谱的解决办法,推荐第一种:
方法一:自定义MongoDB枚举转换器(最通用)
我们需要写两个转换器,分别负责把枚举序列化为MongoDB的字符串,以及把MongoDB的字符串反序列化为枚举,然后注册到Spring Data MongoDB的配置中。
1. 写序列化转换器(枚举转字符串)
这个转换器会在保存数据时,把State枚举转成你定义的小写value:
import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.WritingConverter; // @WritingConverter标记这是写入MongoDB时用的转换器 @WritingConverter public class StateToStringConverter implements Converter<State, String> { @Override public String convert(State source) { // 调用你枚举里的toValue()方法,返回小写的自定义值 return source.toValue(); } }
2. 写反序列化转换器(字符串转枚举)
这个转换器会在读取数据时,把MongoDB里的小写字符串转成对应的State枚举:
import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; // @ReadingConverter标记这是从MongoDB读取时用的转换器 @ReadingConverter public class StringToStateConverter implements Converter<String, State> { @Override public State convert(String source) { // 调用你枚举里的fromValue()方法,根据自定义值匹配枚举 return State.fromValue(source); } }
3. 注册转换器到MongoDB配置
创建一个配置类,把上面两个转换器注册进去:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import java.util.Arrays; @Configuration public class MongoConfig { @Bean public MongoCustomConversions mongoCustomConversions() { return new MongoCustomConversions(Arrays.asList( new StateToStringConverter(), new StringToStateConverter() )); } }
这样配置完成后,MongoDB读写State枚举时就会用你自定义的逻辑:
- 保存数据:
State.GREEN会被存成"vert" - 读取数据:从MongoDB拿到
"vert"会被转成State.GREEN,不会再抛出IllegalArgumentException
方法二:用MongoDB的@Field配合SpEL(简化版,仅适用于单个枚举)
如果不想写转换器,也可以直接在实体类的枚举字段上用SpEL表达式指定存储值:
@Document(collection = "my_collec") public class MyClass { @Id private ObjectId _id; // 保存时用枚举的toValue(),读取时用State.fromValue() @Field(value = "st", targetType = String.class) private State state; // 注意:需要给state字段加对应的getter/setter,或者用Lombok public State getState() { return state; } public void setState(State state) { this.state = state; } // 读取时的转换逻辑:MongoDB的字符串转枚举 @Value("#{T(com.yourpackage.enums.State).fromValue(target.state)}") public void setState(String stateStr) { this.state = State.fromValue(stateStr); } }
不过这种方式不如转换器通用,如果你有多个枚举需要处理,还是推荐第一种方法。
为什么之前的Jackson注解没用?
再补个知识点:Spring Data MongoDB默认用MappingMongoConverter处理对象和MongoDB文档的转换,其中枚举的默认转换器是EnumWritingConverter和EnumReadingConverter——这两个转换器直接调用枚举的name()和Enum.valueOf(),完全忽略Jackson的注解,所以你的Jackson配置对MongoDB读写无效。
内容的提问来源于stack exchange,提问作者Simon30

