Svitlana
Svitlana

Reputation: 1766

Validate object field by conditions using Spring

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

Answers (2)

otterslide
otterslide

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

Svitlana
Svitlana

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

Related Questions