-
Notifications
You must be signed in to change notification settings - Fork 38.6k
Description
Sander van Schouwenburg opened SPR-15082 and commented
In Hibernate Validation 5 it is possible to let a validation error refer to a list element. However, the SpringValidatorAdapter does not cope with this situation.
Take the following code example:
public class ValidatorFactoryTests {
public static class ListContainer {
@NotXList
private List<String> list = new LinkedList<>();
public void addString(String value) {
list.add(value);
}
public List<String> getList() {
return list;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy=NotXListValidator.class)
public static @interface NotXList {
String message() default "Should not be X";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
}
public static class NotXListValidator implements ConstraintValidator<NotXList, List<String>> {
@Override
public void initialize(NotXList constraintAnnotation) {
}
@Override
public boolean isValid(List<String> list, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
boolean valid = true;
for (int i = 0; i < list.size(); i++) {
if ("X".equals(list.get(i))) {
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addBeanNode().inIterable().atIndex(i).addConstraintViolation();
valid = false;
}
}
return valid;
}
}
@Test
public void testSpringValidatorWithListValidation() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
ListContainer listContainer = new ListContainer();
listContainer.addString("A");
listContainer.addString("X");
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(listContainer, "listContainer");
errors.initConversion(new DefaultConversionService());
validator.validate(listContainer, errors);
FieldError fieldError = errors.getFieldError("list[1]");
assertEquals("X", errors.getFieldValue("list[1]"));
}
}
This test will fail on the last line with the following IllegalArgumentException
:
java.lang.IllegalArgumentException: Source to convert from must be an instance of [@org.springframework.validation.beanvalidation.ValidatorFactoryTests$NotXList java.lang.String]; instead it was a [java.util.LinkedList]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:184)
at org.springframework.validation.AbstractPropertyBindingResult.formatFieldValue(AbstractPropertyBindingResult.java:125)
at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:232)
at org.springframework.validation.beanvalidation.ValidatorFactoryTests.testSpringValidatorWithListValidation(ValidatorFactoryTests.java:273)
This IllegalArgumentException
mainly comes from the ConversionService
, but without it the test will still fail, because errors.getFieldValue("list\[1\]")
will result in the entire list, not just the failed element.
The problem (or potential fix) lies in SpringValidatorAdapter.getRejectedValue
. The ConstraintViolation.getInvalidValue
results in the value on which the annotation was defined (in this case the list itself). It seem to try to detect here whether it should it should re-obtain the value in case a custom path was supplied. However, it only checks for the existance of a "." in the fieldname or whether the validation annotation was on the entire command object. In this case the field was list\[1\]
which does not pass the test.
A possible fix is to include the "[" character in the field test.
Affected Versions
I believe this affects all versions which have Hibernate Validation 5 support. Hibernate Validation 4 did not support a constraint violation of this type.
Affects: 4.3 GA
Issue Links:
- SpringValidatorAdapter is incorrectly resolving rejected value for bean based field level constraints [SPR-9332] #13970 SpringValidatorAdapter is incorrectly resolving rejected value for bean based field level constraints
- GenericConversionService.convert() throws IllegalArgumentException after updating to Spring 3.2.1 [SPR-10243] #14876 GenericConversionService.convert() throws IllegalArgumentException after updating to Spring 3.2.1
- NumberFormatException caused by property paths from JSR-303 based validation with no index into a collection [SPR-16177] #20725 NumberFormatException caused by property paths from JSR-303 based validation with no index into a collection