I'm looking to do a little custom validation with JSR-303 javax.validation
.
I have a field. And If a certain value is entered into this field I want to require that a few other fields are not null
.
I'm trying to figure this out. Not sure exactly what I would call this to help find an explanation.
Any help would be appreciated. I am pretty new to this.
At the moment I'm thinking a Custom Constraint. But I'm not sure how to test the value of the dependent field from within the annotation. Basically I'm not sure how to access the panel object from the annotation.
public class StatusValidator implements ConstraintValidator<NotNull, String> { @Override public void initialize(NotNull constraintAnnotation) {} @Override public boolean isValid(String value, ConstraintValidatorContext context) { if ("Canceled".equals(panel.status.getValue())) { if (value != null) { return true; } } else { return false; } } }
It's the panel.status.getValue();
giving me trouble.. not sure how to accomplish this.
@Valid references are only followed when they are not null, so something else must be causing your NPE. Show activity on this post. @Valid references does not seem to be followed only if they are not null.
The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.
Define method that must validate to true and put the @AssertTrue
annotation on the top of it:
@AssertTrue private boolean isOk() { return someField != something || otherField != null; }
The method must start with 'is'.
In this case I suggest to write a custom validator, which will validate at class level (to allow us get access to object's fields) that one field is required only if another field has particular value. Note that you should write generic validator which gets 2 field names and work with only these 2 fields. To require more than one field you should add this validator for each field.
Use the following code as an idea (I've not test it).
Validator interface
/** * Validates that field {@code dependFieldName} is not null if * field {@code fieldName} has value {@code fieldValue}. **/ @Target({TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) @Repeatable(NotNullIfAnotherFieldHasValue.List.class) // only with hibernate-validator >= 6.x @Constraint(validatedBy = NotNullIfAnotherFieldHasValueValidator.class) @Documented public @interface NotNullIfAnotherFieldHasValue { String fieldName(); String fieldValue(); String dependFieldName(); String message() default "{NotNullIfAnotherFieldHasValue.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) @Documented @interface List { NotNullIfAnotherFieldHasValue[] value(); } }
Validator implementation
/** * Implementation of {@link NotNullIfAnotherFieldHasValue} validator. **/ public class NotNullIfAnotherFieldHasValueValidator implements ConstraintValidator<NotNullIfAnotherFieldHasValue, Object> { private String fieldName; private String expectedFieldValue; private String dependFieldName; @Override public void initialize(NotNullIfAnotherFieldHasValue annotation) { fieldName = annotation.fieldName(); expectedFieldValue = annotation.fieldValue(); dependFieldName = annotation.dependFieldName(); } @Override public boolean isValid(Object value, ConstraintValidatorContext ctx) { if (value == null) { return true; } try { String fieldValue = BeanUtils.getProperty(value, fieldName); String dependFieldValue = BeanUtils.getProperty(value, dependFieldName); if (expectedFieldValue.equals(fieldValue) && dependFieldValue == null) { ctx.disableDefaultConstraintViolation(); ctx.buildConstraintViolationWithTemplate(ctx.getDefaultConstraintMessageTemplate()) .addNode(dependFieldName) .addConstraintViolation(); return false; } } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) { throw new RuntimeException(ex); } return true; } }
Validator usage example (hibernate-validator >= 6 with Java 8+)
@NotNullIfAnotherFieldHasValue( fieldName = "status", fieldValue = "Canceled", dependFieldName = "fieldOne") @NotNullIfAnotherFieldHasValue( fieldName = "status", fieldValue = "Canceled", dependFieldName = "fieldTwo") public class SampleBean { private String status; private String fieldOne; private String fieldTwo; // getters and setters omitted }
Validator usage example (hibernate-validator < 6; the old example)
@NotNullIfAnotherFieldHasValue.List({ @NotNullIfAnotherFieldHasValue( fieldName = "status", fieldValue = "Canceled", dependFieldName = "fieldOne"), @NotNullIfAnotherFieldHasValue( fieldName = "status", fieldValue = "Canceled", dependFieldName = "fieldTwo") }) public class SampleBean { private String status; private String fieldOne; private String fieldTwo; // getters and setters omitted }
Note that validator implementation uses BeanUtils
class from commons-beanutils
library but you could also use BeanWrapperImpl
from Spring Framework.
See also this great answer: Cross field validation with Hibernate Validator (JSR 303)
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