Spring Boot conditional validation example.

Balázs Kocsis
2 min readJul 30, 2017

--

Validating user entered data on the frontend is optional, although on the backend is a must. Spring Boot offers a wide range of validation annotations to use, but not out of the box solution for conditional or cross field validation. For this job we can create our own custom annotation, with the following stack :

  • Spring Boot(1.4.2)
  • Maven (3.3.9)
  • IntelliJ IDEA
  • Java 8
  • Lombok library
  • Apache Commons BeanUtils API

Let’s suppose we have the following java class ExportLocationDTO,

which as it’s name indicates is a Data Transfer Object. The main purpose of this class is to transfer the data between the user interface and the database. The locationOptionfield’s type is ExportLocationOption which is just an Enum with three possible values. A name attribute is associated with each entry to make later validation more generic.

The name field is a mandatory and annotated accordingly as well as the ExportLocationOption .

The custom validation logic comes here:

  • if locationOption’s value is EMAILthe emailAddressfield is mandatory
  • if locationOption's value is either FTPor SFTPthe host, port, userand passwordfields are required.

This task can be achieved by a custom class level annotation.

With @Repeatable we can repeat and stack more annotation on the the same class and it is governed by another simple annotation, Conditionals:

Using this validator works as the following:

The actual validation logic is done by the ConditionalValidatorclass.

In the initialize method we assign the variables given in our annotation.
The isValid method contains the concrete validation logic.

Object actualValue = BeanUtils.getProperty(object, selected);

With the help of some reflection and apache’sBeanUtils library, store the validated object’s selected field (in our case this is the locationOption)
in actualValue .

if (Arrays.asList(values).contains(actualValue))

If actualValue is present in the values array of the annotation
then by iterating through the required array using reflection again we assign and check if the field’s value is not null or not empty.

for (String propName : required) {
Object requiredValue = BeanUtils.getProperty(objectToValidate, propName);
valid = requiredValue != null && !isEmpty(requiredValue);

Another important part is , since this a class level annotation used for field level validation the default constraint violation has to be overridden in order add a custom one. With this we can register a new ConstraintViolation with
a message and field associated with.

if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(propName).addConstraintViolation();
}

And finally let’s see some test cases.

The full project can be found here: https://github.com/balag3/ConditionalValidator

--

--

Balázs Kocsis

Full Stack Engineer | Smart Contract Security Enthusiast