You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Jackson将Enum序列化为String以触发自定义校验返回自定义消息

问题根因

Spring MVC 处理 @RequestBody 请求的流程是先由 Jackson 反序列化 JSON 字符串为 Java 对象,反序列化失败时会直接抛出 HttpMessageNotReadableException,被异常处理器转为 400 Bad Request,此时还未执行 Bean Validation 的校验逻辑,因此你的自定义校验器完全没有触发机会。

可行解决方案

该方案完全保留枚举类型定义,不会影响 Swagger 生成枚举下拉列表,仅调整反序列化逻辑让非法值可以走到校验阶段:

步骤1:实现自定义枚举反序列化器

作用是遇到非法枚举值时不抛出异常,返回 null 让反序列化流程正常走完,进入后续校验阶段,同时可存储非法值用于优化报错信息:

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 TypeEnumDeserializer extends StdDeserializer<Type> {
    // 存储用户输入的非法枚举值,用于校验时生成报错信息
    public static final ThreadLocal<String> INVALID_ENUM_VALUE = new ThreadLocal<>();

    public TypeEnumDeserializer() {
        super(Type.class);
    }

    @Override
    public Type deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        String value = jsonParser.getText().trim();
        for (Type type : Type.values()) {
            if (type.name().equals(value)) {
                INVALID_ENUM_VALUE.remove();
                return type;
            }
        }
        // 非法值存入ThreadLocal,返回null进入校验逻辑
        INVALID_ENUM_VALUE.set(value);
        return null;
    }
}

步骤2:给枚举绑定自定义反序列化器

直接在你的 Type 枚举上加注解指定反序列化器,枚举类型本身未发生变化,Swagger 仍可正常识别枚举值:

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize(using = TypeEnumDeserializer.class)
public enum Type {
    MISSING_SITE,
    INACTIVE_SITE;
}

步骤3:调整自定义校验器逻辑

原来的校验逻辑存在冗余:能被反序列化为 Type 类型的参数必然是合法枚举值,无需二次遍历判断。现在仅需判断反序列化结果为 null 的场景即可:

public class ReportTypeValidator implements ConstraintValidator<ValidReportType, Type> {
    private String globalMessage;

    @Override
    public void initialize(ValidReportType constraintAnnotation) {
        globalMessage = constraintAnnotation.message();
    }

    @Override
    public boolean isValid(Type type, ConstraintValidatorContext constraintValidatorContext) {
        if (type == null) {
            String invalidValue = TypeEnumDeserializer.INVALID_ENUM_VALUE.get();
            StringBuilder errorMsg = new StringBuilder(globalMessage + ", report type does not exist");
            if (invalidValue != null) {
                errorMsg.append(", invalid value: ").append(invalidValue);
            }
            constraintValidatorContext
                    .buildConstraintViolationWithTemplate(errorMsg.toString())
                    .addConstraintViolation();
            TypeEnumDeserializer.INVALID_ENUM_VALUE.remove();
            return false;
        }
        TypeEnumDeserializer.INVALID_ENUM_VALUE.remove();
        return true;
    }
}

步骤4:调整校验注解作用范围(按需调整)

如果你的 Type 类型是定义在 CustomerParameter 请求实体的字段上,需要给 @ValidReportType 注解的 @Target 增加字段类型支持:

@Constraint(validatedBy = ReportTypeValidator.class)
@Target( { ElementType.PARAMETER, ElementType.FIELD }) // 新增FIELD作用范围
@Retention(RetentionPolicy.RUNTIME)
@Valid
public @interface ValidReportType {
    String message() default "Invalid value for report type";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

然后在 CustomerParameter 类的 Type 类型字段上加上 @ValidReportType 注解即可生效。


内容的提问来源于stack exchange,提问作者mr.Penguin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 01:45:03