I have a simple bean with enum
field
public class TestBean{
@Pattern(regexp = "A|B") //does not work
private TestEnum testField;
//getters + setters
}
enum TestEnum{
A, B, C, D
}
I would like to validate testField
using Bean Validation. Specifically I would like to make sure that only A and B values are allowed (for a particular calidation gropus). It seems that enums are not handled JSR 303 (I was trying to use @Pattern validator) or I am doing something in a wrong way.
I am getting exception:
javax.validation.UnexpectedTypeException: No validator could be found for type: packagename.TestEnum
Is there any way to validate enum fields without writing custom validator?
If you want to put the constraint on testField you need a custom validator. None of the default ones handle enums.
As a workaround you could add a getter method which returns the string value of the enum
public class TestBean{
private TestEnum testField;
//getters + setters
@Pattern(regexp = "A|B") //does not work
private String getTestFieldName() {
return testField.name();
}
}
A custom validator is probably the cleaner solution though.
Since for some reasons enumerations are not supported this restriction could be simply handled by a simple String based Validator.
Validator:
/**
* 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());
}
}
Interface:
@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 uses apache commons library. An advanced coerce to type method would enhance the flexibility of this validator even further.
An alternative could use a single String attribute instead of an array and split by delimiter. This would also print values nicely for error-message since arrays are not being printed, but handling null values could be a problem using String.valueOf(...)
I'd like to share my working solution:
@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;
}
来源:https://stackoverflow.com/questions/10445197/jsr-303-bean-validation-for-enum-fields