校验枚举类型

ゝ一世哀愁。 2022-09-04 13:47 299阅读 0赞

文章目录

  • 1.介绍
  • 2.校验枚举
  • 3.校验枚举的类型
  • 4.校验枚举的子集
  • 5.验证字符串是否匹配枚举的值

1.介绍

使用自定义注解校验枚举类型

2.校验枚举

大多数标准注解都不支持枚举的校验。

例如

当将 @Pattern 注解应校验枚举时, Hibernate Validator会报以下错误:

javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint ‘javax.validation.constraints.Pattern’ validating type ‘com.niugang.javabase.enums.CustomerTypeEnum’. Check configuration for ‘customerTypeMatchesPattern’

实际上,可以应用于枚举的唯一标准注解是@NotNull 和@Null。

3.校验枚举的类型

准备

  1. public enum CustomerTypeEnum {
  2. /** * */
  3. NEW,
  4. OLD,
  5. EDIT,
  6. DEFAULT;
  7. }

定义一个注解来校验枚举的类型:

  1. import javax.validation.ConstraintValidator;
  2. import javax.validation.ConstraintValidatorContext;
  3. import java.util.regex.Matcher;
  4. import java.util.regex.Pattern;
  5. import java.util.regex.PatternSyntaxException;
  6. public class EnumNamePatternValidator implements ConstraintValidator<EnumNamePattern, Enum<?>> {
  7. private Pattern pattern;
  8. @Override
  9. public void initialize(EnumNamePattern annotation) {
  10. try {
  11. pattern = Pattern.compile(annotation.regexp());
  12. } catch (PatternSyntaxException e) {
  13. throw new IllegalArgumentException("错误的正则", e);
  14. }
  15. }
  16. @Override
  17. public boolean isValid(Enum<?> value, ConstraintValidatorContext context) {
  18. if (value == null) {
  19. return true;
  20. }
  21. Matcher m = pattern.matcher(value.name());
  22. return m.matches();
  23. }
  24. }

实现与标准的@Pattern 验证器非常相似。 但是,这一次,匹配了枚举的名称。

验证Bean

  1. @Data
  2. public class UserDto {
  3. @NotNull(message = "姓名不能为空")
  4. private String name;
  5. @AssertTrue
  6. private boolean working;
  7. @Size(min = 10, max = 200, message
  8. = "关于我在10到200字符")
  9. private String aboutMe;
  10. @Min(value = 18, message = "年龄最小18岁")
  11. @Max(value = 150, message = "年龄最大150岁")
  12. private int age;
  13. @Email(message = "邮箱无效")
  14. private String email;
  15. @EnumNamePattern(regexp = "NEW|DEFAULT")
  16. private CustomerTypeEnum customerType;
  17. }

测试

  1. UserDto user = new UserDto();
  2. user.setName("张三");
  3. user.setWorking(true);
  4. user.setAboutMe("来自北京的张三,住在朝阳区");
  5. user.setAge(50);
  6. user.setCustomerType(CustomerTypeEnum.EDIT);
  7. ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
  8. Validator validator = factory.getValidator();
  9. Set<ConstraintViolation<UserDto>> violations = validator.validate(user);
  10. for (ConstraintViolation<UserDto> violation : violations) {
  11. System.out.println(violation);
  12. }

ConstraintViolationImpl{interpolatedMessage=‘请匹配 “NEW|DEFAULT”’, propertyPath=customerType, rootBeanClass=class com.niugang.javabase.pojo.UserDto, messageTemplate=‘请匹配 “{regexp}”’}

4.校验枚举的子集

将枚举与正则表达式匹配不是类型安全的。 相反,与枚举的实际值进行比较更有意义。

但是,由于注解的限制,这样的注解不能通用。 这是因为注解的参数只能是特定枚举的具体值,而不是枚举父类的实例。

为 CustomerTypeEnum 枚举创建特定的子集校验注解:

  1. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  2. @Retention(RUNTIME)
  3. @Documented
  4. @Constraint(validatedBy = CustomerTypeSubSetValidator.class)
  5. public @interface CustomerTypeSubset {
  6. CustomerTypeEnum[] anyOf();
  7. String message() default "必须为任何一个 {anyOf}";
  8. Class<?>[] groups() default { };
  9. Class<? extends Payload>[] payload() default { };
  10. }
  11. @CustomerTypeSubset(anyOf = { CustomerTypeEnum.NEW, CustomerTypeEnum.OLD})
  12. private CustomerTypeEnum customerType;

需要定义 CustomerTypeSubSetValidator 来检查给定枚举值的列表是否包含当前值:

  1. public class CustomerTypeSubSetValidator implements ConstraintValidator<CustomerTypeSubset, CustomerTypeEnum> {
  2. private CustomerTypeEnum[] subset;
  3. @Override
  4. public void initialize(CustomerTypeSubset constraint) {
  5. this.subset = constraint.anyOf();
  6. }
  7. @Override
  8. public boolean isValid(CustomerTypeEnum value, ConstraintValidatorContext context) {
  9. return value == null || Arrays.asList(subset).contains(value);
  10. }
  11. }

5.验证字符串是否匹配枚举的值

创建一个注解来检查字符串对于特定枚举是否有效。

  1. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
  2. @Retention(RUNTIME)
  3. @Documented
  4. @Constraint(validatedBy = ValueOfEnumValidator.class)
  5. public @interface ValueOfEnum {
  6. Class<? extends Enum<?>> enumClass();
  7. String message() default "必须为 {enumClass},中的枚举值";
  8. Class<?>[] groups() default { };
  9. Class<? extends Payload>[] payload() default { };
  10. }

校验逻辑

  1. public class ValueOfEnumValidator implements ConstraintValidator<ValueOfEnum, CharSequence> {
  2. private List<String> acceptedValues;
  3. @Override
  4. public void initialize(ValueOfEnum annotation) {
  5. acceptedValues = Stream.of(annotation.enumClass().getEnumConstants())
  6. .map(Enum::name)
  7. .collect(Collectors.toList());
  8. }
  9. @Override
  10. public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
  11. if (value == null) {
  12. return true;
  13. }
  14. return acceptedValues.contains(value.toString());
  15. }
  16. }
  17. @ValueOfEnum(enumClass = CustomerTypeEnum.class)
  18. private String customerTypeString;

这种验证在处理 JSON 对象时特别有用。 因为出现了下面的异常,当把不正确的值从 JSON 对象映射到枚举时:

发表评论

表情:
评论列表 (有 0 条评论,299人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C#——类型

    C\——枚举类型 枚举类型 是由基础整型数值类型的一组命名常量定义的值类型。 若要定义枚举类型,请使用 enum 关键字并指定枚举成员 的名称: enum Se

    相关 类型

    枚举类型的定义和枚举变量的说明  1.  枚举的定义枚举类型定义的一般形式为:      enum 枚举名\{ 枚举值表 \};  在枚举值表中应罗列出所有可用值。