javabean-validationjakarta-validation

Can you apply Bean Validation to arbitrary field types?


I can't find it explicitly stated in any of the documentation for JSR303/JSR380 bean validation (Jakarta Validation), but do all fields to which you apply validations have to be "essentially" plain-old-data (POD) types, like Strings and ints and Longs and such?

I tried to apply a regular expression validator to a UUID field, and I got a NoProviderFoundException thrown. I worked around it by just changing that field to a String and putting the onus of converting the field into a UUID on the consumer(s) of that class, but is there any way to create a custom "bean validation provider" mapped to an arbitrary non-POD class for cases where implementing a workaround isn't as straightforward?

Note: java.util.UUID is a serializable class, and therefore perfectly valid to include in a Bean.


Solution

  • Bean validation works for types, as long as the constraint annotation has a matching ConstraintValidator defined for it. I don't think that you can add support for extra types for the out-of-the-box annotations, but if you create your own custom annotation you can support all types you want.

    A quick-and-dirty example based on @Pattern:

    @Target({ElementType.FIELD, ElementType.METHOD}) // add others as needed
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = UuidValidator.class) // one for each type you want to support
    @Documented
    public @interface UuidPattern {
    
        String regexp();
    
        // mandated properties
        String message() default "UUID doesn't match pattern {regexp}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    
        public class UuidValidator implements ConstraintValidator<UuidPattern, UUID> {
    
            private Pattern pattern;
    
            @Override
            public void initialize(UuidPattern annotation) {
                pattern = Pattern.compile(annotation.regexp());
            }
    
            @Override
            public boolean isValid(UUID uuid, ConstraintValidatorContext constraintValidatorContext) {
                if (uuid == null) {
                    // let @NotNull handle this case
                    return true;
                }
                return pattern.matcher().matches(uuid.toString());
            }
        }
    }