问题
If I have a class and I want to check if the input to a property in that class is in a list, how would I do this?
Example
public class Length {
public static final List<String> ALLOWABLE_UNITS =
Collections.unmodifiableList(Arrays.asList("inches", "feet", "meters", "millimeters"));
public BigDecimal lengthValue;
@SomeMatchingAnnotation(ALLOWABLE_UNITS)
public String lengthUnit;
}
Is there an annotation to do that? Would I have to create my own?
回答1:
In the setter for your field you could do the validation like this:
public void setLengthUnit(String lengthUnit) {
if (!ALLOWABLE_UNITS.contains(lengthUnit) {
throw new IllegalArgumentException("Length unit not recognized.");
}
this.lengthUnit = lengthUnit;
}
回答2:
If you are using JSR-303 to validate your beans then you can look for some custom validations, write yourself one, or just use javax.validation.constraints.Pattern
:
@Pattern(message="your message here" , regexp="^(meter|inch|cm)$")
public String lengthUnit;
update
If you want to pass dynamically built array of correct values then you should rather use custom validation. If you use Hibernate Validator
as JSR-303 provider then you can alternatively use the @ScriptAssert
annotation which allows you to define constraints using JSR-223 compatible scripting engine:
@ScriptAssert(lang = "javascript", script = "_this.possibleValues.indexOf(_this.lengthUnit) > -1")
Please note that I did not run above example, but it should work as expected.
Alternatively you can add constraint in on-the-fly mode (still assuming that you use Hibernate Validation).
ConstraintMapping customMapping = new ConstraintMapping();
customMapping.type(Length.class).property("lengthUnit", FIELD).constraint(new PatternDef().regexp("^(mm|cm|inch)$"));
HibernateValidatorConfiguration cfg = Validation.byProvider(HibernateValidator.class).configure();
cfg.addMapping(customMapping);
ValidatorFactory vf = cfg.buildValidatorFactory();
Validator validator = vf.getValidator()
回答3:
Writing a custom Hibernate Validator is a good choice. If you elect to use it, place the hibernate-validator
JAR on your classpath. I'm using the most recent version for this example, 5.1.1.Final.
There's a lot of code that has to be shown for it to work, but it's really three parts:
- An annotation on the field to specify that this is the element that needs to be validated,
- The standard implementation of the annotation itself, and
- The implementation of the
ConstraintValidator
for that annotation and object type.
First, the bean:
public class Length {
@ValidUnit // this is the annotation we will use to validate.
private String inputUnit;
public String getInputUnit() {
return inputUnit;
}
public void setInputUnit(final String inputUnit) {
this.inputUnit = inputUnit;
}
}
The field is a String
; we'll need to remember that for later.
Next, the annotation. Hibernate mandates that a message()
, groups()
, and a payload()
be present. Only the message is required by JSR-303, I believe. So, we'll keep the implementation vanilla and only implement the few things we believe we need.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = ValidUnitValidator.class)
public @interface ValidUnit {
String message() default "Not a supported unit.";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
The @Constraint
annotation above this class tells Hibernate which class to use for the validation of the type. So, let's implement that.
The declaration for anything that implements ConstraintValidator
is an annotation and a type T
. We're trying to validate String
, so we put that in place of T
in our implementation.
public class ValidUnitValidator implements ConstraintValidator<ValidUnit, String> {
private final Set<String> validUnits = new HashSet<>(Arrays.asList("inches", "feet", "meters", "millimeters"));
@Override
public void initialize(final ValidUnit validUnit) {
}
@Override
public boolean isValid(final String value, final ConstraintValidatorContext constraintValidatorContext) {
return validUnits.contains(value);
}
}
Ultimately, it's a bit ceremonial compared to the more straightforward approach of validating in-line. However, this gives you a neater separation of concerns; should you want to change what is a valid unit (you may want to add light-years next), you only need to modify and test one class.
来源:https://stackoverflow.com/questions/24727176/java-validation-from-list