Mary
Mary

Reputation: 1595

Custom validator that compares two fields of a bean

I would like to create a custom validator that validates a field called description. The validation of this field is dependent on another field in this bean called category- if category is Other then description cannot be null or empty, if category is not Other then description has to be null or empty. I havent written a custom validator before and would appreciate any help. This is what I have so far (1) Create the @interface

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; 
import javax.validation.Constraint;
import javax.validation.Payload;
@Target(METHOD, FIELD, ANNOTATION_TYPE )
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=CategoryDescriptionValidator.class)
public @interface CategoryDescription {
String message() default "{description should be empty when category is not other}";
Class<?>[] groups() default {};
}

(2) Create the implementation - here I am confused as to how do I specify that the fields being compared are the Object's category and description fields?

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CategoryDescriptionValidator implements ConstraintValidator<CategoryDescription,Object>{
@Override
public void initialize(CategoryDescription arg0) {
    // TODO Auto-generated method stub

}
@Override
public boolean isValid(Object arg0, ConstraintValidatorContext arg1) {
    // TODO Auto-generated method stub
    return false;
}

}

(3) In my bean(InformationBean) should I just do the following?

 public class InformationBean{
 private String category;
 @CategoryDescriptionValidator 
 private String description;
}

Upvotes: 0

Views: 4279

Answers (1)

Adam Dyga
Adam Dyga

Reputation: 8926

Since the validation involves two object fields, you need to place the constraint on the enclosing class. This makes sense, since now you are actually validating (part of) the object, not a single field. So you have to do sth like this:

@CategoryDescription
public class InformationBean{
 private String category; 
 private String description;
}

Having done that, you can now implement the isValid() method. If the validator should work only for this single specific class (InformationBean) you can parameterize the validator like this:

public class CategoryDescriptionValidator implements ConstraintValidator<CategoryDescription ,InformationBean>{
  @Override
  public void initialize(CategoryDescription arg0) {
   // ...
  }

  @Override
  public boolean isValid(InformationBean arg0, ConstraintValidatorContext arg1) {
    // validation code here 
  }
}

As you can see the first argument to isValid() is your bean, so you can easily get the category and description and implement proper validation logic. This is also the reason to place the annotation on the class - otherwise you wouldn't be able to access both fields. If annotation is placed on a field you have acces only to value of the annotated field.

If you want the validator to work for any object, you can still do this using the validator you presented (using Object as the second generic param), but then you have to get the fields to be accessed in a different way (eg. using reflection).

EDIT: To put the @CategoryDescription constraint on the class, you need to change @Target(METHOD, FIELD, ANNOTATION_TYPE) to @Target(TYPE) in constraint definition

Upvotes: 2

Related Questions