枚举字段的JSR-303 Bean验证

时间:2012-05-04 08:19:28

标签: java-ee java-ee-6 bean-validation

我有一个带enum字段的简单bean

public class TestBean{
   @Pattern(regexp = "A|B") //does not work
   private TestEnum testField;
   //getters + setters
}

enum TestEnum{
  A, B, C, D
}

我想使用Bean Validation验证testField。具体来说,我想确保只允许A和B值(对于特定的calidation gropus)。似乎没有处理枚举JSR 303(我试图使用@Pattern验证器)或者我做错了。

我得到例外:

javax.validation.UnexpectedTypeException: No validator could be found for type: packagename.TestEnum

有没有办法在不编写自定义验证器的情况下验证枚举字段?

3 个答案:

答案 0 :(得分:7)

由于某些原因不支持枚举,因此可以通过简单的基于字符串的验证器简单地处理此限制。

<强>验证

/**
 * Validates a given object's String representation to match one of the provided
 * values.
 */
public class ValueValidator implements ConstraintValidator<Value, Object>
{
    /**
     * String array of possible enum values
     */
    private String[] values;

    @Override
    public void initialize(final Value constraintAnnotation)
    {
        this.values = constraintAnnotation.values();
    }

    @Override
    public boolean isValid(final Object value, final ConstraintValidatorContext context)
    {
        return ArrayUtils.contains(this.values, value == null ? null : value.toString());
    }
}

<强>接口

@Target(value =
{
    ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy =
{
    ValueValidator.class
})
@Documented
public @interface Value
{
    public String message() default "{package.Value.message}";

    Class<?>[] groups() default
    {};

    Class<? extends Payload>[] payload() default
    {};

    public String[] values() default
    {};
}

Validator使用apache commons库。高级强制类型方法将进一步增强此验证器的灵活性。

替代方法可以使用单个String属性而不是数组,并使用分隔符进行拆分。这也可以很好地为错误消息打印值,因为没有打印数组,但使用String.valueOf(...)

处理空值可能是一个问题

答案 1 :(得分:6)

如果要将约束放在 testField 上,则需要自定义验证器。没有默认值处理枚举。

作为一种解决方法,您可以添加一个getter方法,该方法返回枚举

的字符串值
public class TestBean{
   private TestEnum testField;
   //getters + setters

   @Pattern(regexp = "A|B") //does not work
   private String getTestFieldName() {
       return testField.name();
   }
}

自定义验证器可能是更清洁的解决方案。

答案 2 :(得分:4)

我想分享我的工作解决方案:

@Documented
@Constraint(validatedBy = { EnumValueValidator.class })
@Retention(RetentionPolicy.RUNTIME)

@Target({ 
    ElementType.ANNOTATION_TYPE, 
    ElementType.CONSTRUCTOR,
    ElementType.FIELD, 
    ElementType.METHOD, 
    ElementType.PARAMETER 
})

public @interface EnumValue
{
    public abstract String                             message() default "{validation.enum.message}";

    public abstract Class<?>[]                         groups()  default {};

    public abstract Class<? extends Payload>[]         payload() default {};

    public abstract Class<? extends java.lang.Enum<?>> enumClass();
}

public class EnumValueValidator implements ConstraintValidator<EnumValue, Object>
{
    private Object[] enumValues;

    @Override
    public void initialize(final EnumValue annotation)
    {
        enumValues = annotation.enumClass().getEnumConstants();
    }

    @Override
    public boolean isValid(final Object value, final ConstraintValidatorContext context)
    {
        if (null != value) {
            String contextValue = value.toString();

            for (Object enumValue : enumValues) {
                if (enumValue.toString().equals(contextValue)) {
                    return true;
                }
            }
        }

        return false;
    }