Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger javax validations manually via reflection or any other way?

I am using validation-api-2.0.1.Final and hibernate-validator-6.0.13.Final. I would like to do validation for the below case,

I have created a custom validation to validate List<Map<String,Object>>

BookInfo.java

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
        validatedBy = {BookInfoValidator.class}
)
public @interface BookInfo {
    String message() default "Should not be empty";

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

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

BookInfoValidator.java

public class BookInfoValidator implements ConstraintValidator<ValidateUserInfo, List<Map<String, Object>>> {

    private final ContentRepositoryClient contentRepository;

    public ValidateUserInfoValidator(ContentRepositoryClient contentRepository) {
        this.contentRepository = contentRepository;
    }

    @Override
    public void initialize(ValidateUserInfo constraintAnnotation) {

    }

    @Override
    public boolean isValid(List<Map<String,Object>> map, ConstraintValidatorContext constraintValidatorContext) {
        //In the list of Map the key will be "text,email,date etc etc" based on the key i would like to
        //validate with the proper validation constraints
        //ex) for Email invoke javax.validation.constraints.Email.class from validation-api
        //I am not sure how to manually invoke the validation annotations.
        return false;
    }
}

BookInfoView.java

class BookInfoView {
        @BookInfo
        private List<Map<String, Object>> bookInfos;
    }

In the list of Map the key will be "text, email, date etc". Based on the key I would like to validate with the proper validation constraints exception for Email invoke javax.validation.constraints.Email.class from validation-api. I am not sure how to manually invoke the validation annotations.

Any hint or help will be much appreciated.

like image 738
VelNaga Avatar asked Jan 01 '23 16:01

VelNaga


2 Answers

I am not sure how to manually invoke the validation annotations.

I am answering above quoted lines. Yes, it is possible to invoke validation programmatically and in case of validation failures you will receive all failure messages in a set. Below are the steps to do the same:

  1. Build ValidatorFactory
  2. Get hold of a Validator instance from ValidatorFactory
  3. Perform the validation using validate() method
  4. Process the validation result constraintViolations.iterator().next().getMessage()

Show some code, below is the code snippet for all four steps mentioned above:

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<BookInfoView>> constraintViolations = validator.validate(bookInfoViewObj);
assertEquals( "Should not be empty", constraintViolations.iterator().next().getMessage() );

Hibernate Validator framework provides various other capabilities to validate one or more entities and then process the result. Better you have look at official document.

like image 155
Atul Dwivedi Avatar answered Jan 05 '23 16:01

Atul Dwivedi


There's no 'nice' way of accessing validator implementations for constraints (email , not null etc.). While you could create instances of those validators and store them in your BookInfoValidator you would need to do a lot of additional work. As for each validator its ConstraintValidator#initialize() method. While in case of simple constraints like @NotNull there's actually noting to initialize, the same check can easily be performed without this validator. And in case of a more complex ones like @Email you would need to create your own proxy class for the annotation so you could properly initialize the constraint validator.

With that said I would suggest to write a wrapper class for your Map, something like:

public class BookInfoWrapper {
    private final Map<String, Object> data;

    public BookInfoWrapper(Map<String, Object> data) {
        this.data = data;
    }

    @NotNull
    public Map<String, Object> getUser(){
        return (Map<String, Object>) data.get( "user" );
    }

    @Email
    public String getEmail(){
        return Objects.toString(( getUser() ).get( "email" ));
    }

    // and any other constraints you need
}

And then convert your list of maps to these wrappers before validation.

I can also see that you have a repository in your validator, hence I think that you might want to derive rules "dynamically". In such case you might want to check out the programmatic API provided by Hibernate Validator. Using it you should be able to build the rules you need based on the data retrieved from the database. But still you would need to wrap the maps first.

To summarize it all, sadly there's no nice and easy solution for your particular case yet. We are working on a validation of free form objects but it'll take us some time to be able to release it. Hence I would suggest that you should either

  • write the validation checks on your own in your BookInfoValidator without using built-in constraints.
  • use a wrapper approach described above.
like image 44
mark_o Avatar answered Jan 05 '23 16:01

mark_o