I have a model like below. Existing code already has validations on individual properties. Now I have a requirement to ignore existing validations on billing_country and address if buyer.method
is foo
. I was thinking I could have a custom validator at buyer
level, check for method and invoke validations only when buyer.method!=foo
. Is this a valid approach? Are there any better alternatives?
"buyer": {
"method": "foo",
"instruments": [
{
"card": {
"type": "MASTERCARD",
"number": "234234234234234",
"expire_month": "12",
"expire_year": "2017",
"billing_country" : "US"
"Address" : {
"line1": "summer st",
"city": "Boston"
"country": "US"
}
}
}
]
}
There's two ways to do this.
You can either
create a custom validation annotation and validator which checks the value of method
and then applies to appropriate validations to the other fields
use validation groups, where you manually check the value of method
, then choose which group to use; your annotated fields will then need to be changed to only be applied when that group is active.
Option #1
Define a class-level annotation:
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { MethodAndInstrumentsValidator.class })
@Documented
public @interface ValidMethodAndInstruments {
String message() default "{my.package.MethodAndInstruments.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Define a validator:
public class MethodAndInstrumentsValidator
implements ConstraintValidator<ValidMethodAndInstruments, Buyer> {
public final void initialize(final ValidMethodAndInstruments annotation) {
// setup
}
public final boolean isValid(final Buyer value,
final ConstraintValidatorContext context) {
// validation logic
if(!"foo".equals(value.getMethod())) {
// do whatever conditional validation you want
}
}
}
Annotate the buyer class:
@ValidMethodAndInstruments
public class Buyer { }
Option #2
Here, you'll have to invoke the validation manually.
First, define the validation groups:
public interface FooValidation {}
public interface NotFooValidation {}
Configure a validator:
@Bean
public LocalValidatorFactoryBean validatorFactory() {
return new LocalValidatorFactoryBean();
}
In your controller(?), check the value of method
and do the validation:
@Autowired
private Validator validator;
// ...
public void validate(Object a, Class<?>... groups) {
Set<ConstraintViolation<Object>> violations = validator.validate(a, groups);
if(!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
// ...
validate(buyer, "foo".equals(buyer.getMethod())
? FooValidation.class
: NotFooValidation.class);
Finally, modify the groups in your model/dto class:
public class Buyer {
// ...
@Null(groups = FooValidation.class)
@NotNull(groups = NotFooValidation.class)
protected String billingCountry;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With