I know hibernate validator supports TYPE_USE
annotations: though it does not define its own, it lets you define and use custom ones.
I could define and validate correctly such an annotation (code soon), but then I want to map the error into a path that is used to display the error to the user.
Given then following sample
public class SampleTest {
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public static class LimitedSizeStringValidator implements ConstraintValidator<LimitedSize, String> {
private LimitedSize constraint;
@Override
public void initialize(LimitedSize constraintAnnotation) {
this.constraint = constraintAnnotation;
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
String s = Ensure.notNull(value);
return s.length() >= constraint.min() &&
s.length() <= constraint.max();
}
}
@Retention(RUNTIME)
@Documented
@Target({TYPE_USE})
@Constraint(validatedBy = {LimitedSizeStringValidator.class})
public @interface LimitedSize {
String message() default "{javax.validation.constraints.Size.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int min() default 0;
int max() default Integer.MAX_VALUE;
}
private static class TestBean {
@Valid
private Collection<@LimitedSize(max = 3) String> strings = new ArrayList<>();
@Valid
private Collection<InnerBean> beans = new ArrayList<>();
}
private static class InnerBean {
@Min(3)
private final int value;
private InnerBean(int value) {
this.value = value;
}
}
@Test
public void testBeanInvalid() {
TestBean testBean = new TestBean();
assertThat(validator.validate(testBean)).isEmpty();
testBean.strings.add("ok");
testBean.strings.add("ok2");
testBean.beans.add(new InnerBean(4));
assertThat(validator.validate(testBean)).isEmpty();
testBean.strings.add("not_ok");
testBean.beans.add(new InnerBean(2));
Set<ConstraintViolation<TestBean>> violations = validator.validate(testBean);
assertThat(violations).hasSize(2);
StreamSupport.stream(violations.spliterator(), false)
.forEach(v -> {
System.out.println(v.getPropertyPath());
System.out.println(v.getMessage());
v.getPropertyPath().forEach(p -> System.out.print("'" + p.getName() + (p.getIndex() != null ? "[" + p.getIndex() + "]" : "") + "' -> "));
System.out.println();
});
}
}
I would like map the errors in an object like
errors: [
["beans", "1", "value"],
["strings", "2"]
]
As in my sample, my approach at the moment is by navigating the violation path (http://docs.oracle.com/javaee/7/api/javax/validation/ConstraintViolation.html#getPropertyPath--) which works perfectly for the first case, but fails for the second (I cannot find a way to retrieve the index of the failing object). I think the reason is in the implementation of javax.validation.Path.PropertyNode in hibernate-validator (I am currently on version 5.2.4.Final
, and the code looks the same as in the linked 5.2.1.Final
. For reference:
@Override
public final Integer getIndex() {
if ( parent == null ) {
return null;
}
else {
return parent.index;
}
}
With TYPE_USE
this approach cannot work in my opinion, because the failing object is a leaf, thus no child node can retrieve the index from it.
Nice enough, hibernate implementation of javax.validation.Path
overrides the toString
method is way such that violation.getPropertyPath().toString()
is beans[1].value
and strings[2]
(in the sample code above).
So, to the question(s): is my navigation approach wrong and there is another way to extract such a mapping from the ConstraintViolation
? Or is this a feature request for hibernate developers (I can see that before TYPE_USE
annotations the getIndex
approach they implemented was totally fine?
It just feels strange I am the first one with this problem (I tried to google and could not find anything related, the closest being: https://github.com/hibernate/hibernate-validator/pull/441) so I am wondering whether the mistake is mine rather than a hibernate limitation
I agree that the index should be set for that value and think you uncovered an issue in Hibernate Validator. Could you open an issue in our JIRA tracker?
Btw. the notion of TYPE_USE level constraints will be standardized as of Bean Validation 2.0. So there may be some more changes coming up in this area, specifically I'm wondering what Kind
that node should have (currently it's PROPERTY
which seems questionable).