Search code examples
javahibernate-validator

How to interpolate the ConstraintValidator message on a Custom Validation Annotation


I've read some Q&As and guides (like Hibernate Documentation) as to how to work with Hibernate's ConstrainValidator, but none of them mention clearly how to interpolate a value in a specific position of a validation error message when creating your own custom validation annotation.

For example, if I have a validation error message that looks like this:

foo.bar.error=This value '{myValue}' is wrong.

I would like to obtain the following message if the validation fails:

The value 'some wrong value' is wrong.

The validation would be used like this:

public class SomeClass {

    @CustomAnnotation(message="{foo.bar.error}")
    public MyObject myObject;

    ...

}

public class MyObject {
    private String myValue;

    ...
}

Solution

  • I've found a way to interpolate the message without much fuzz.

    First set your ValidationMessages.properties (or wherever your validation messages are stored) as before:

    foo.bar.error=This value '{wrongValue}' is wrong.
    

    Create your annotation like you normally would:

    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    @Documented
    @Constraint(validatedBy = CustomValueValidator.class)
    @Target(FIELD)
    @Retention(RUNTIME)
    public @interface CustomAnnotation {    
        String message() default "{foo.bar.error}";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    

    Implement your constraint validator like this:

    import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    public class CustomValueValidator 
            implements ConstraintValidator<CustomAnnotation, MyObject> {
    
        @Override
        public void initialize(final CustomAnnotation constraintAnnotation) {
            // Extract any value you would need from the annotation
        }
    
        @Override
        public boolean isValid(final MyObject myObject, 
                               final ConstraintValidatorContext context) {
            boolean valid;
    
            // Add your validations over the 'myObject' object
    
            if (!valid) {
                ((ConstraintValidatorContextImpl) context)
                        .addMessageParameter("wrongValue", myObject.getMyValue());
            }
    
            return valid;
        }
    }
    

    Now all that is left is to use the annotation:

    public class SomeClass {
    
        // I'm using the default message. You could override it as a parameter
        @CustomAnnotation
        public MyObject anotherObject;
    
        ...
    
    }