Skip to content

Spring validation crashes with Hibernate Validation 5 style list constraint violations [SPR-15082] #19648

@spring-projects-issues

Description

@spring-projects-issues

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:

Referenced from: commits d0e9328, de611cb

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions