Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSR 303 Validation, If one field equals "something", then these other fields should not be null

I'm looking to do a little custom validation with JSR-303 javax.validation.

I have a field. And If a certain value is entered into this field I want to require that a few other fields are not null.

I'm trying to figure this out. Not sure exactly what I would call this to help find an explanation.

Any help would be appreciated. I am pretty new to this.

At the moment I'm thinking a Custom Constraint. But I'm not sure how to test the value of the dependent field from within the annotation. Basically I'm not sure how to access the panel object from the annotation.

public class StatusValidator implements ConstraintValidator<NotNull, String> {      @Override     public void initialize(NotNull constraintAnnotation) {}      @Override     public boolean isValid(String value, ConstraintValidatorContext context) {         if ("Canceled".equals(panel.status.getValue())) {             if (value != null) {                 return true;             }         } else {             return false;         }     } } 

It's the panel.status.getValue(); giving me trouble.. not sure how to accomplish this.

like image 258
Eric Avatar asked Feb 14 '12 21:02

Eric


People also ask

Can @valid be null?

@Valid references are only followed when they are not null, so something else must be causing your NPE. Show activity on this post. @Valid references does not seem to be followed only if they are not null.

What is the use of @valid annotation?

The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.


2 Answers

Define method that must validate to true and put the @AssertTrue annotation on the top of it:

  @AssertTrue   private boolean isOk() {     return someField != something || otherField != null;   } 

The method must start with 'is'.

like image 54
Audrius Meškauskas Avatar answered Nov 18 '22 23:11

Audrius Meškauskas


In this case I suggest to write a custom validator, which will validate at class level (to allow us get access to object's fields) that one field is required only if another field has particular value. Note that you should write generic validator which gets 2 field names and work with only these 2 fields. To require more than one field you should add this validator for each field.

Use the following code as an idea (I've not test it).

  • Validator interface

    /**  * Validates that field {@code dependFieldName} is not null if  * field {@code fieldName} has value {@code fieldValue}.  **/ @Target({TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) @Repeatable(NotNullIfAnotherFieldHasValue.List.class) // only with hibernate-validator >= 6.x @Constraint(validatedBy = NotNullIfAnotherFieldHasValueValidator.class) @Documented public @interface NotNullIfAnotherFieldHasValue {      String fieldName();     String fieldValue();     String dependFieldName();      String message() default "{NotNullIfAnotherFieldHasValue.message}";     Class<?>[] groups() default {};     Class<? extends Payload>[] payload() default {};      @Target({TYPE, ANNOTATION_TYPE})     @Retention(RUNTIME)     @Documented     @interface List {         NotNullIfAnotherFieldHasValue[] value();     }  } 
  • Validator implementation

    /**  * Implementation of {@link NotNullIfAnotherFieldHasValue} validator.  **/ public class NotNullIfAnotherFieldHasValueValidator     implements ConstraintValidator<NotNullIfAnotherFieldHasValue, Object> {      private String fieldName;     private String expectedFieldValue;     private String dependFieldName;      @Override     public void initialize(NotNullIfAnotherFieldHasValue annotation) {         fieldName          = annotation.fieldName();         expectedFieldValue = annotation.fieldValue();         dependFieldName    = annotation.dependFieldName();     }      @Override     public boolean isValid(Object value, ConstraintValidatorContext ctx) {          if (value == null) {             return true;         }          try {             String fieldValue       = BeanUtils.getProperty(value, fieldName);             String dependFieldValue = BeanUtils.getProperty(value, dependFieldName);              if (expectedFieldValue.equals(fieldValue) && dependFieldValue == null) {                 ctx.disableDefaultConstraintViolation();                 ctx.buildConstraintViolationWithTemplate(ctx.getDefaultConstraintMessageTemplate())                     .addNode(dependFieldName)                     .addConstraintViolation();                     return false;             }          } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {             throw new RuntimeException(ex);         }          return true;     }  } 
  • Validator usage example (hibernate-validator >= 6 with Java 8+)

    @NotNullIfAnotherFieldHasValue(     fieldName = "status",     fieldValue = "Canceled",     dependFieldName = "fieldOne") @NotNullIfAnotherFieldHasValue(     fieldName = "status",     fieldValue = "Canceled",     dependFieldName = "fieldTwo") public class SampleBean {     private String status;     private String fieldOne;     private String fieldTwo;      // getters and setters omitted } 
  • Validator usage example (hibernate-validator < 6; the old example)

    @NotNullIfAnotherFieldHasValue.List({     @NotNullIfAnotherFieldHasValue(         fieldName = "status",         fieldValue = "Canceled",         dependFieldName = "fieldOne"),     @NotNullIfAnotherFieldHasValue(         fieldName = "status",         fieldValue = "Canceled",         dependFieldName = "fieldTwo") }) public class SampleBean {     private String status;     private String fieldOne;     private String fieldTwo;      // getters and setters omitted } 

Note that validator implementation uses BeanUtils class from commons-beanutils library but you could also use BeanWrapperImpl from Spring Framework.

See also this great answer: Cross field validation with Hibernate Validator (JSR 303)

like image 42
Slava Semushin Avatar answered Nov 19 '22 00:11

Slava Semushin