0

I can validate a nested object using an annotation and validator, implementing ConstraintValidator. The code has a conditional validation statement, where it should validate a nested object.

It is done like so:

 @Override
    public boolean isValid(ScheduleJobRequest value, ConstraintValidatorContext context) {
        if (value.getJobType() != JobType.EXPORT) {
            return true; // do not fail
        }
        final ExportRequestData exportData = value.getExportData();
        if (exportData == null) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("NotEmpty")
                    .addPropertyNode("exportData").addConstraintViolation();
            return false;    
        }
        
        final Set<ConstraintViolation<ExportRequestData>> violations = validator.validate(exportData);
        violations.forEach(violation -> context
                .buildConstraintViolationWithTemplate(violation.getMessage())
                .addConstraintViolation());
        return violations.isEmpty();
    }

So in case the jobType enum is EXPORT, it should evaluate exportData which is another DTO.

I also have a custom error handler; which works fine on top level validations. This is done by overwriting handleMethodArgumentNotValid:

I read the BindingResult there and its FieldErrors. However, I notice for my nested object in this case, it does not appear in the FieldErrors.

(code snippet):

    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {

        BindingResult bindingResult = ex.getBindingResult();
        final List<FieldError> fieldErrors = bindingResult.getFieldErrors();
// this does not show any errors for nested objects

Ideally I want the result to be a path to the property which is invalid. FieldName could be the name itself.

What did I miss?

BTW: whenever I try to set propertyNode in the validator, like so:

        violations.forEach(violation -> context
                .buildConstraintViolationWithTemplate(violation.getMessage())
                .addPropertyNode(violation.getPropertyPath().toString())
                .addConstraintViolation());

I get an error as well (java.lang.IllegalStateException: JSR-303 validated property .... does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)) which I also can't seem to resolve. (I tried several SO answers).

Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
Stefan Hendriks
  • 4,705
  • 5
  • 34
  • 43

1 Answers1

0

I found the problem and how to fix it. The nested object must have an annotation @Valid within my parent object. I did not do that because it seemed a duplication to me. I also do not want to validate the object everytime, and I thought @Valid would act like a this is a required field. But it doesn't.

Adding the annotation works and gives me expected path properties as a bonus.

This answer helped give me some context.

Stefan Hendriks
  • 4,705
  • 5
  • 34
  • 43