A little greedy question here, hope this one could also help others who want to know more about annotation validation
I am currently studying Spring, and for now, I
For this you can use a type level annotation only because a field level annotation has no access to other fields!
I did something similar to allow a choice validation (exactly one of a number of properties has to be not null). In your case the @AllOrNone
annotation (or whatever name you prefer) would need an array of field names and you will get the whole object of the annotated type to the validator:
@Target(ElementType.TYPE)
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = AllOrNoneValidator.class)
public @interface AllOrNone {
String[] value();
String message() default "{AllOrNone.message}";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
public class AllOrNoneValidator implements ConstraintValidator {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private String[] fields;
@Override
public void initialize(AllOrNone constraintAnnotation) {
fields = constraintAnnotation.value();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
long notNull = Stream.of(fields)
.map(field -> PARSER.parseExpression(field).getValue(value))
.filter(Objects::nonNull)
.count();
return notNull == 0 || notNull == fields.length;
}
}
(As you said you use Spring I used SpEL to allow even nested fields access)
Now you can annotate your Subscriber
type:
@AllOrNone({"birthday", "confirmBirthday"})
public class Subscriber {
private String name;
private String email;
private Integer age;
private String phone;
private Gender gender;
private Date birthday;
private Date confirmBirthday;
private String birthdayMessage;
private Boolean receiveNewsletter;
}