Domingo De Abreu Domingo De Abreu - 2 months ago 11
Java Question

Jackson Conditional Validation of a field

I'm trying to set up a Range Query structure on Spring with the following structure:

public class QueryRange extends Query {

@JsonProperty(required = true)
@NotBlank
@NotNull
private String field;

private Object gt;

private Object gte;

private Object lt;

private Object lte;

public String getField() {
return field;
}

public QueryRange setField(String field) {
this.field = field;
return this;
}

public Object getGt() {
return gt;
}

public QueryRange setGt(Object gt) {
this.gt = gt;
return this;
}

public Object getGte() {
return gte;
}

public QueryRange setGte(Object gte) {
this.gte = gte;
return this;
}

public Object getLt() {
return lt;
}

public QueryRange setLt(Object lt) {
this.lt = lt;
return this;
}

public Object getLte() {
return lte;
}

public QueryRange setLte(Object lte) {
this.lte = lte;
return this;
}

@AssertTrue(message = "Invalid range. A value for 'gt' or 'lt' should be set.")
private boolean isRangeSpecified() {
return this.gt != null || this.lt != null;
}

@AssertTrue(message = "Invalid range data type. 'gt' and 'lt' must be of the same type.")
private boolean isRangeDataTypeValid() {
if (this.gt != null && this.lt != null) {
return this.gt.getClass().equals(this.lt.getClass());
}

return true;
}
}


The problem is that at least one of the values gt, gte, lt or lte should be not null. How can I validate that?

I've tried Bean Validation like on the code example above, but it didn't work. Any suggestions?

Answer

You should use Class-level constraint.

Last but not least, a constraint can also be placed on the class level. In this case not a single property is subject of the validation but the complete object. Class-level constraints are useful if the validation depends on a correlation between several properties of an object.

@ValidQueryRange
public class QueryRange extends Query {
    // your class here...
    private Object gt;

    private Object gte;

    private Object lt;

    private Object lte;
}

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { ValidQueryRangeValidator.class })
@Documented
public @interface ValidQueryRange {

    String message() default "{error}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };
}

public class ValidQueryRangeValidator implements ConstraintValidator<ValidQueryRange, QueryRange> {

    @Override
    public void initialize(ValidQueryRange constraintAnnotation) {
    }

    @Override
    public boolean isValid(QueryRange queryRange, ConstraintValidatorContext context) {
        if ( queryRange == null ) {
            return true;
        }
        // put your logic here
        if ( queryRange.getGt() == null || queryRange.getLt() == null) {
            return false;
        }

        return true;
    }
}