0

I want to create condition based validation in spring validator. I have one UserDTO class in that there are two DTO class with @Valid annotation.

If I pass isPrimary true then it should validate only primaryDTO bean and ignoring secendoryDTO validations.

public class UserDTO {
    @Valid
    private PrimaryDTO primaryDTO;
    @Valid
    private SecendoryDTO secendoryDTO;
    private boolean isPrimary;
}

public class PrimaryDTO {
    @NotEmpty(message = "Please enter email.")
    @Email(message = "Please enter a valid email.")
    private String email;
}

public class SecendoryDTO {
    @NotEmpty(message = "Please enter phone.")
    private String phone;
}

Please Guide.

Thanks

Parth Solanki
  • 3,268
  • 2
  • 22
  • 41

1 Answers1

4

If your validation depends on multiple fields (eg. isPrimary and either primaryDTO or secondaryDTO), then the only solution is to write a custom validator on classlevel (UserDTO) which will implement the conditional validation itself.

For example, create an annotation:

@Documented
@Retention(RUNTIME)
@Target({ANNOTATION_TYPE, TYPE})
@Constraint(validatedBy = SecondaryValidator.class)
public @interface ValidSecondary {
    String message() default "Invalid secondary";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

And create a validator that only validates the secondaryDTO field when isPrimary() is false:

@Component
public class SecondaryValidator implements ConstraintValidator<ValidSecondary, UserDTO> {
    private Validator validator;

    public SecondaryValidator(Validator validator) {
        this.validator = validator;
    }

    @Override
    public boolean isValid(UserDTO userDTO, ConstraintValidatorContext constraintValidatorContext) {
        if (userDTO.isPrimary()) {
            return true;
        } else {
            return validator.validate(userDTO.getSecondaryDTO()).isEmpty();
        }
    }
}

After that, you can remove the @Valid annotation from the secondaryDTO field and add the @ValidSecondary annotation on top of your UserDTO:

@ValidSecondary // Add this
public class UserDTO {
    @Valid
    private PrimaryDTO primaryDTO;
    private SecondaryDTO secondaryDTO; // No more @Valid
    private boolean primary;
}

However, in this case you'll lose any constraint violation message from within the SecondaryDTO, if you want to have some kind of passing through mechanism, you can add the violations to the constraintValidatorContext within the isValid() method, for example:

Set<ConstraintViolation<SecondaryDTO>> violations = validator.validate(userDTO.getSecondaryDTO());
violations.forEach(violation -> constraintValidatorContext
    .buildConstraintViolationWithTemplate(violation.getMessage())
    .addConstraintViolation());
g00glen00b
  • 41,995
  • 13
  • 95
  • 133