简体   繁体   中英

TYPE_USE annotation in hibernate validator

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM