Java 枚举使用 Jackson Json 序列化与反序列化

我最近遇到一个问题,Jackson Json 无法正常反序列化枚举类,尽管同一个枚举可以正常序列化。我查阅了一些资料并写下了这篇总结。

问题

enum Type{
    A(11),
    B(22);
    @JsonValue
    public final int code;
    Type(int code) {
        this.code = code;
    }
}

上述枚举类可以正常序列化,序列化的结果是 code 字段的值:

objectMapper.writeValueAsString(Type.A); //值为11

但在反序列化时会报错:

objectMapper.readValue("11", Type.class); //报错

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.example.Example` from number 11: index value outside legal index range [0..1]

在网上搜索后,我发现这种方式应该是可行的。经过一系列排查,我发现问题出在当前使用的版本(2.11.x)无法支持 Integer 类型的反序列化,而 String 类型可以正常反序列化,例如 A("11")。以下,我将介绍几种常见的枚举序列化与反序列化的方法。

使用 @JsonValue 注解

在字段或方法上使用 @JsonValue 注解可以让 Jackson Json 进行序列化与反序列化。但是,如果你的 Jackson 版本是 2.13.0 之前的版本,可能会遇到一些问题,例如无法对 Integer 类型进行反序列化,如上述例子所示。此时,你需要使用 @JsonCreator 自定义生成方法。

使用 @JsonCreator 自定义生成方法

在上述例子中,我们可以定义一个 static 方法(此处必须是 static 的),这样就可以从数字成功反序列化。

@JsonCreator
public static Type create(int code) {
    for (Type value : Type.values()) {
        if (value.code == code) {
           return value;
        }
    }
    return null;
}

使用 @JsonProperty 自定义值

你也可以使用 @JsonProperty 注解来标注序列化与反序列化的值,但这种方法可能会比较麻烦,尤其是当枚举属性太多时。例如:

enum Type{
    @JsonProperty("11")
    A(11),
    @JsonProperty("22")
    B(22);
    //...
}

自定义 serializer 和 deserializer

当然,你也可以自定义 serializer 和 deserializer,这是最灵活的方法,但可能过于复杂,具体方法可以参阅这篇文章。在这里就不再赘述。

@JsonValue with integer for enum does not deserialize correctly · Issue #1850 · FasterXML/jackson-databind (github.com)

Jackson - Custom Serializer and Deserializer (howtodoinjava.com)