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

1 Answer 1

5

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());
Sign up to request clarification or add additional context in comments.

3 Comments

I'm getting an error Caused by: java.lang.NoSuchMethodException : SecondaryValidator.<init>() in this class SecondaryValidator
@ParthSolanki You should probably add @Autowired to the constructor then.
I try to make the messages bubble up, but they don't seem to appear as FieldErrors in the BindingResult? Any clue?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.