如何强制Jackson对Null和缺失属性使用@JsonCreator反序列化?
解决Jackson枚举反序列化时Null/缺失字段不调用@JsonCreator的问题
问题场景
使用包含枚举字段的Java Record类进行JSON反序列化时,出现如下不符合预期的情况:
- 当枚举字段值为
null时,Jackson不会调用枚举类的@JsonCreator方法,直接将字段赋值为null - 当枚举字段缺失时,同样直接赋值为
null
需要让null值和缺失字段都通过@JsonCreator方法处理,得到预设的默认枚举值(如示例中的RED)。
模型类代码
record MyRec( String first, Color second ){} // --- enum Color{ RED, GREEN; @JsonCreator static Color fromName(String name){ if (name == null || "red".equals(name)){ return RED; } else if ("green".equals(name)) { return GREEN; } else { throw new IllegalArgumentException(name); } } }
反序列化结果对比
| JSON内容 | 实际结果 | 预期结果 | 状态 |
|---|---|---|---|
{"first": "hi", "second": "red"} | MyRec("hi", RED) | MyRec("hi", RED) | 正常 |
{"first": "hi", "second": null} | MyRec("hi", null) | MyRec("hi", RED) | 异常 |
{"first": "hi"} | MyRec("hi", null) | MyRec("hi", RED) | 异常 |
解决方案
方案1:修改Record构造逻辑处理Null值
利用Java Record的紧凑构造器,在构造实例时主动处理null的枚举字段,直接调用枚举的fromName方法生成默认值:
record MyRec(String first, Color second) { // 紧凑构造器:自动处理所有构造参数 public MyRec { if (second == null) { // 调用@JsonCreator方法处理null情况 second = Color.fromName(null); } } }
原理:不管JSON中枚举字段是null还是缺失,Jackson都会将该字段初始化为null传入构造器,此时在构造器中统一调用fromName(null)即可得到预期的RED值。
方案2:自定义枚举反序列化器
通过自定义反序列化器接管Color类型的所有反序列化逻辑,包括null值和缺失字段的处理:
步骤1:编写自定义反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; public class ColorDeserializer extends StdDeserializer<Color> { public ColorDeserializer() { super(Color.class); } @Override public Color deserialize(JsonParser parser, DeserializationContext context) throws IOException { String value = parser.getText(); // 直接复用枚举的fromName方法处理所有情况(包括null) return Color.fromName(value); } }
步骤2:给枚举类绑定反序列化器
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @JsonDeserialize(using = ColorDeserializer.class) enum Color{ RED, GREEN; @JsonCreator static Color fromName(String name){ if (name == null || "red".equals(name)){ return RED; } else if ("green".equals(name)) { return GREEN; } else { throw new IllegalArgumentException(name); } } }
步骤3:处理缺失字段
为了让缺失字段也触发反序列化逻辑,需要给Record的second字段设置默认值,通过添加额外的构造器实现:
record MyRec(String first, Color second) { // 当second字段缺失时,调用此构造器生成默认值 public MyRec(String first) { this(first, Color.fromName(null)); } }
原理:自定义反序列化器会处理所有传入的Color类型值(包括null),而额外的构造器则确保字段缺失时主动生成预期的默认枚举值。
内容的提问来源于stack exchange,提问作者diziaq
相关产品推荐
相关产品推荐

