Jackson中请求参数中枚举(enum)类型字段反序列化实践

背景

前后端接口交互中,经常会出现一些枚举值,比如订单状态。前端请求参数一般是整数值,相应地,后端接收参数也用int类型,然后再转换成对应的枚举类型。

每次都需要手动转换不免有点麻烦,有没有什么办法可以让接收到的int值自动转换成我想要的枚举值呢?

实现

定义一个接口,表示可以用int获取code的枚举类型:

public interface CodeEnum {
    int getCode();
}

定义枚举类,实现这个接口:

public enum Direction implements CodeEnum {
    NORTH(0),
    EAST(1),
    SOUTH(2),
    WEST(3);

    Direction(int code) {
        this.code = code;
    }

    private final int code;

    @Override
    public int getCode() {
        return code;
    }
}

定义一个反序列化类,继承JsonDeserializer、实现ContextualDeserializer接口

JsonDeserializer:用于自定义反序列化器,将JSON的字段值反序列化成我们想要的类型,在这里我们需要把int表示的枚举值反序列化成具体枚举类的枚举值

ContextualDeserializer:用于获取反序列字段的类型

@Slf4j
public class CodeEnumDeserializer extends JsonDeserializer implements ContextualDeserializer {

    private Class propertyClass; // 记录枚举字段的类,用于获取其定义的所有枚举值

    public CodeEnumDeserializer() {
        log.info("Construct with no args"); // 必须有无参构造器,spring会调用
    }

    public CodeEnumDeserializer(Class propertyClass) {
        this.propertyClass = propertyClass;
    }

    @Override
    public CodeEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        int code;
        try {
            code = p.getIntValue();
        } catch (JsonParseException e) {
            return null;
        }
        log.info("Enum code: {}", code);
        return Arrays.stream(propertyClass.getEnumConstants()) // 调用Class的这个方法,获取枚举类的所有枚举值
                .filter(e -> e.getCode() == code)
                .findAny()
                .orElseThrow(() -> new IllegalArgumentException("No such code of " + propertyClass.getSimpleName()));
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws
            JsonMappingException {
        log.info("Construct with property class");
        return new CodeEnumDeserializer((Class) property.getType().getRawClass()); // 获取枚举字段的类型Class
    }

}

接收请求的类定义:

@Data
public class TestReq {

    private String name;

    @JsonDeserialize(using = CodeEnumDeserializer.class) // 给字段指定定义的反序列化器
    private Direction direction;

}

也可以利用@JsonComponent注解配置成全局的,只需要在CodeEnumDeserializer类上加@JsonComponent(参考:Spring Boot中使用@JsonComponent

你可能感兴趣的