Reputation: 1766
I have a complex object that contains two UserPropertyForm
objects inside:
public class ComplexUserForm {
int userType;
@Valid
UserPropertyForm property1;
UserPropertyForm property2;
...
}
public class UserPropertyForm {
@NotEmpty
@Length(max = 255)
private String title;
@NotEmpty
@Length(min = 100)
private String description;
...
}
I need property1
be validated every time, so I have marked it as @Valid
.
I need property2
be validated only if userType == 2
Could anyone say if I can validate property2
in a simple way using annotations I have for UserPropertyForm
fields?
Thanks for any help.
Upvotes: 3
Views: 3078
Reputation: 560
You can use this custom annotation above your class.
@ValidateIfAnotherFieldHasValue(
fieldName = "userType",
fieldValue = "2",
dependFieldName = "property2")
public class ComplexUserForm {
int userType;
@Valid
UserPropertyForm property1;
UserPropertyForm property2;
It will validate property2 only when getUserType().equals("2").
The error messsages will go in property2.fieldname so you'll need
<form:errors path="property2.*"/>
in your JSP if you want to catch all errors together from property2.
public class ValidateIfAnotherFieldHasValueValidator
implements ConstraintValidator<ValidateIfAnotherFieldHasValue, Object> {
private String fieldName;
private String expectedFieldValue;
private String dependFieldName;
@Override
public void initialize(final ValidateIfAnotherFieldHasValue annotation) {
fieldName = annotation.fieldName();
expectedFieldValue = annotation.fieldValue();
dependFieldName = annotation.dependFieldName();
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext ctx) {
if (value == null) {
return true;
}
try {
final String fieldValue = BeanUtils.getProperty(value, fieldName);
final Object dependFieldValue = PropertyUtils.getProperty(value, dependFieldName);
if (expectedFieldValue.equals(fieldValue)) {
ctx.disableDefaultConstraintViolation();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Object>> errorList = validator.validate(dependFieldValue);
for(ConstraintViolation<Object> error : errorList) {
ctx.buildConstraintViolationWithTemplate(error.getMessageTemplate())
.addNode(dependFieldName+"."+error.getPropertyPath())
.addConstraintViolation();
}
return errorList.isEmpty();
}
} catch (final NoSuchMethodException ex) {
throw new RuntimeException(ex);
} catch (final InvocationTargetException ex) {
throw new RuntimeException(ex);
} catch (final IllegalAccessException ex) {
throw new RuntimeException(ex);
}
return true;
}
}
and:
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidateIfAnotherFieldHasValueValidator.class)
@Documented
public @interface ValidateIfAnotherFieldHasValue {
String fieldName();
String fieldValue();
String dependFieldName();
String message() default "{ValidateIfAnotherFieldHasValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
ValidateIfAnotherFieldHasValue[] value();
}
}
Upvotes: 4
Reputation: 1766
I've manage to do that in validate
method of form's validator:
public void validate(final Object obj, final Errors errors) {
final ComplexUserForm form = (ComplexUserForm) obj;
if (form.getUserType() == 2) {
ClassValidator<UserPropertyForm> offered2Validator = new ClassValidator<UserPropertyForm>(UserPropertyForm.class);
InvalidValue[] property2InvalidValues = property2Validator.getInvalidValues(form.getProperty2());
for (final InvalidValue invalidValue : property2InvalidValues)
errors.rejectValue("property2." + invalidValue.getPropertyPath(), invalidValue.getMessage(), invalidValue.getMessage());
}
}
}
But I had to add "property2."
string to the value's path when rejecting some value of property2
field. If someone knows better way I would be glad to know it. Thanks
Upvotes: 1