0
votes

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

1 Answers

1
votes

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)