Jackson枚举反序列化异常:为何需同时使用@JsonCreator与@JsonValue?
低版本Jackson枚举反序列化需配合@JsonCreator的原因
核心差异:Jackson版本对@JsonValue的处理逻辑变化
Jackson 2.11之前(如你使用的2.10.2):
@JsonValue仅作用于序列化阶段——它告诉Jackson将枚举实例序列化为getCode()返回的数值,但反序列化时,Jackson的默认逻辑并不会自动逆向使用这个规则。此时Jackson会优先按枚举的ordinal(声明顺序的序号,从0开始)来匹配输入值,其次才会尝试匹配枚举名称。
你的DeliveryType枚举中,A的ordinal是0、code是1;B的ordinal是1、code是2。所以反序列化输入1时,Jackson会匹配ordinal=1的B,输入0匹配ordinal=0的A,完全忽略了code的值。这时候必须通过@JsonCreator标注静态工厂方法fromCode(),明确告诉Jackson反序列化时要根据code值查找对应的枚举实例。Jackson 2.11及之后(如你升级的2.15.2):
官方优化了枚举反序列化逻辑:当枚举类存在@JsonValue注解时,Jackson会自动启用逆向映射——遍历所有枚举实例,用输入值匹配每个实例的@JsonValue返回值,找到对应的枚举。因此不需要额外添加@JsonCreator,Jackson就能自动根据code值完成反序列化。
你的枚举代码示例
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstructor; import lombok.Getter; import java.util.stream.Stream; @Getter @AllArgsConstructor public enum DeliveryType { A (1, "Delivery A", "X"), B (2, "Delivery B", "Y"); private final Integer code; private final String description; private final String textCode; @JsonCreator public static DeliveryType fromCode(final Integer code) { return Stream.of(DeliveryType.values()) .filter(type -> type.code == code) .findFirst() .orElse(null); } @JsonValue public Integer getCode() { return code; } }
测试代码示例
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class DeliveryTypeTest { private final ObjectMapper objectMapper; private final ObjectWriter writer; public DeliveryTypeTest() { this.objectMapper = new ObjectMapper(); writer = objectMapper.writerWithDefaultPrettyPrinter(); } @Test public void serialize() throws JsonProcessingException { final DeliveryType type = DeliveryType.A; final String json = writer.writeValueAsString(type); Assertions.assertEquals(DeliveryType.A.getCode().toString(), json); } @Test public void deserialize() throws JsonProcessingException { final String jsonValue = DeliveryType.A.getCode().toString(); final DeliveryType deSerializedType = objectMapper.readValue(jsonValue, DeliveryType.class); Assertions.assertEquals(DeliveryType.A, deSerializedType); } }
内容的提问来源于stack exchange,提问作者DJViking
相关产品推荐
相关产品推荐

