Getting this error HV000030: No validator could be found for constraint EnumChecker validating type MyEnum Check configuration for ‘myenum’
.
Using the annotation on this class to be validated, I can't do private final String myenum
because I need to use it as an enum in order to use the function inside the enum
public class Request {
@EnumChecker(enumclass = MyEnum.class)
private final MyEnum myenum;
}
annotation
@Documented
@Constraint(validatedBy = MyEnumValidator.class)
@Target({FIELD, PARAMETER, TYPE, TYPE_USE, TYPE_PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface EnumChecker {
String message() default "must be one of these values";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum> enumClass();
}
validator
public class MyEnumValidator implements ConstraintValidator<EnumChecker, String> {
private List<String> validEnumList;
@Override
public void initialize(EnumChecker constraintAnnotation) {
validEnumList = Arrays.stream(constraintAnnotation.enumClass().getEnumConstants())
.map(Enum::name)
.collect(Collectors.toList());
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return validEnumList.contains(value);
}
}
enum
public enum MyEnum {
YES, NO;
private static final Map<MyEnum, String> MyEnumConverter =
Map.of(YES, "yes");
public String toMyString() {
return MyEnumConverter.get(this);
}
}
First delete final
modifier in myenum
and create setter and getter for that field, because Jackson
is not able to bind values from http request to your Request
class for example.
public class Request {
@EnumChecker(enumClass = MyEnum.class)
private MyEnum myenum;
public MyEnum getMyenum() {
return myenum;
}
public void setMyenum(MyEnum myenum) {
this.myenum = myenum;
}
}
Then change ConstraintValidator
target type to match MyEnum
instead of String
public class MyEnumValidator implements ConstraintValidator<EnumChecker, MyEnum> {
private List<MyEnum> validEnumList;
@Override
public void initialize(EnumChecker constraintAnnotation) {
validEnumList = Arrays.stream(constraintAnnotation.enumClass().getEnumConstants())
.collect(Collectors.toList());
}
@Override
public boolean isValid(MyEnum value, ConstraintValidatorContext context) {
return validEnumList.contains(value);
}
}
Test: I did the following test
@RestController
public class MainController {
@PostMapping("/annot")
public String filter(@RequestBody @Valid Request req) {
return req.getMyenum().name();
}
}
Also I changed the implementation of MyEnumValidator
to only see validation effect and prove that it takes place. (I admit that currently this validator does not make sense. Also using only @RequestBody
guarantees that myenum
can only Have YES
or NO
values otherwise throws exception)
public class MyEnumValidator implements ConstraintValidator<EnumChecker, MyEnum> {
private List<MyEnum> validEnumList;
@Override
public void initialize(EnumChecker constraintAnnotation) {
validEnumList = Arrays.stream(constraintAnnotation.enumClass().getEnumConstants())
.collect(Collectors.toList());
}
@Override
public boolean isValid(MyEnum value, ConstraintValidatorContext context) {
return validEnumList.contains(value) && value.name().length() == 3;
}
}
For a invalid case:
stacktrace:
Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String api.sweater.controller.MainController.filter(api.sweater.request.Request): [Field error in object 'request' on field 'myenum': rejected value [NO]; codes [EnumChecker.request.myenum,EnumChecker.myenum,EnumChecker.api.sweater.request.MyEnum,EnumChecker]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [request.myenum,myenum]; arguments []; default message [myenum],class api.sweater.request.MyEnum]; default message [must be one of these values]] ]