0

I want to validate a JSON array against JSR 303 annotations with Hibernate Validator. While the validation works for JSON object and properties of array type (with @Valid), the validation is skipped for elements of a top-level JSON array.

For example:

public class ValidationTest {

    public static void main(String[] args) throws IOException {
        ObjectMapper m = new ObjectMapper();
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

        System.out.println(validator.validate(m.readValue("{}", Person.class)));
        System.out.println(validator.validate(m.readValue("[{}]", Person[].class)));
        System.out.println(validator.validate(m.readValue("{\"array\":[{}]}", PersonArray.class)));
    }

}

class Person {
    @NotNull
    private String name;

    public String getName() {
        return name;
    }

    public Person setName(String name) {
        this.name = name;
        return this;
    }
}

class PersonArray {
    @Valid
    private Person[] array;

    public Person[] getArray() {
        return array;
    }

    public PersonArray setArray(Person[] array) {
        this.array = array;
        return this;
    }
}

Output:

[ConstraintViolationImpl{interpolatedMessage='must not be null', propertyPath=name, rootBeanClass=class com.radius.Person, messageTemplate='{javax.validation.constraints.NotNull.message}'}]
[]
[ConstraintViolationImpl{interpolatedMessage='must not be null', propertyPath=array[0].name, rootBeanClass=class com.radius.PersonArray, messageTemplate='{javax.validation.constraints.NotNull.message}'}]

As you can see, the required name property is validated for Person and recursively for array inside PersonArray, but not for Person[]. Is there a way to have recursive validation for a top-level JSON array?

1 Answer 1

1

Digging in the hibernate-validator and validation-api jars I found that you need validation annotations in your class in order to trigger the validation process and the Person[].class does not have any so the recursive validation for the array elements does not get initiated. It seems that BeanDescriptor.isBeanConstrained() returns false for your example.

   /**
     * Returns {@code true} if the bean involves validation:
     * <ul>
     *     <li>a constraint is hosted on the bean itself</li>
     *     <li>a constraint is hosted on one of the bean properties</li>
     *     <li>or a bean property is marked for cascaded validation ({@link Valid})</li>
     * </ul>
     * <p>
     * Constrained methods and constructors are ignored.
     *
     * @return {@code true} if the bean involves validation, {@code false} otherwise
     */
    boolean isBeanConstrained();

An easy and non-intrusive fix is a class like the following:

import javax.validation.Valid;

public class ValidWrapper<T> {

    @Valid
    private T[] data;

    public ValidWrapper(final T[] _data) {
        this.data = _data;
    }

    public T[] getData() {
        return data;
    }
}

Then you can validate any array like:

validator.validate(m.readValue("{\"data\": [{}]}", ValidWrapper.class)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the lead! Do you know if I can configure the validator on Person[] so it will do the same thing as @Valid?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.