Categories
Java

class based JSR303 – validator adding error on property

Sometimes it is necessary to validate one property or multiple properties of a class at one time or in relation to each other, but you want to add a constraint violation to the specific property. The solution will described in this post

You have your model:

@ValidateFieldIfFlag
public class Model {
    @Id
    @GeneratedValue
    private int id;

    private boolean flag;

    private String field;

    ... // getter and setter following
}

As you see the class uses a custom validator ValidateFieldIfFlag to validate a Model instance.

So you have the annotation:

@Constraint(validatedBy = {ValidateFieldIfFlagValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateFieldIfFlagValidator
{
	String message() default "my error message";

	Class<?>[] groups() default {};

	Class<? extends Payload>[] payload() default {};
}

And your validator:

public class ValidateFieldIfFlagValidator implements ConstraintValidator<ValidateFieldIfFlagValidator, Model>
{
    public boolean isValid(Model model, ConstraintValidatorContext context) {
        if (!model.isFlag()) return true;
        //if flag is set, field should be nonEmpty      
        return model.getField() != null && !model.getField().isEmpty();
    }
}

The validator will validate the field to be non empty, if the flag is set. Unfortunately you will receive a ConstraintViolationException for the whole instance and not only for the field. To change this, you have to modify the validator:

public class ValidateFieldIfFlagValidator implements ConstraintValidator<ValidateFieldIfFlagValidator, Model>
{
    public boolean isValid(Model model, ConstraintValidatorContext context) {
        if (!model.isFlag()) return true;
        //if flag is set, field should be nonEmpty      
        if (model.getField() != null && !model.getField().isEmpty()) return true;
        ConstraintValidatorContext.ConstraintViolationBuilder constraintViolationBuilder = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
        constraintViolationBuilder.addPropertyNode("field").addConstraintViolation();
        context.disableDefaultConstraintViolation();        
        return false;
    }
}

This suppress the default, class matched ConstraintViolationException, and creates a ConstraintViolationException matching the property “field”.