javax validation greater or less than from other property
Asked Answered
U

2

13

with respect to javax.validation

  @NotNull(message = "From can't be null")
  @Min(value = 1, message = "From must be greater than zero")
  private Long from;
  @NotNull(message = "To can't be null")
  @Min(value = 1, message = "To must be greater than zero")
  private Long to;

I want to also validate that FROM should be less than TO and TO should be greater than FROM ? how we can do this using javax validation's annotation ?

Unguentum answered 24/5, 2018 at 6:12 Comment(2)
#38225233Kourtneykovac
check above linkKourtneykovac
N
5

You need a custom cross field validation annotation.

One way is to annotate your custom class with @YourCustomAnnotation.

In YourCustomAnnotationValidator you have access to your value, hence you can implement your logic there:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Constraint(validatedBy = DateValidator.class)
public @interface RangeCheck {

    String message();

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

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

public class RangeCheckValidtor implements ConstraintValidator<RangeCheck, YourDto> {

    @Override
    public void initialize(RangeCheck date) {
        // Nothing here
    }

    @Override
    public boolean isValid(YourDto dto, ConstraintValidatorContext constraintValidatorContext) {
        if (dto.getFrom() == null || dto.getTo() == null) {
            return true;
        }
        return from < to;
    }
}

Then mark your YourDto class with @RangeCheck:

@RangeCheck(message = "your messgae")
public class YourDto {
   // from
   // to
}

Or simply manually validate the relation of two fields.

Nonsectarian answered 24/5, 2018 at 6:21 Comment(0)
C
0

For anyone who needs a more generic solution, validator can be implemented to compare two given property. Here is what I came up with :

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { CompareFieldsValidator.class })
public @interface CompareFields {

    String message() default "{CompareFields.message}";

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

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

    String left();

    String right();
    
    int[] vals() default {0};

    @Target({ TYPE, ANNOTATION_TYPE })
    @Retention(RUNTIME)
    public @interface List {
        CompareFields[] value();
    }
}


public class CompareFieldsValidator implements ConstraintValidator<CompareFields, Object> {

    private static final Logger log = LoggerFactory.getLogger(FieldsEqualityValidator.class);

    private String left;
    private String right;
    private Set<Integer> vals;

    @Override
    public void initialize(CompareFields constraintAnnotation) {
        left = constraintAnnotation.left();
        right = constraintAnnotation.right();
        vals = Arrays.stream(constraintAnnotation.vals()).boxed().collect(Collectors.toSet());
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null)
            return true;

        try {
            Class<?> clazz = value.getClass();
            //get left operand as comparable
            Field leftField = ReflectionUtils.findField(clazz, left);
            leftField.setAccessible(true);
            Object leftObj = leftField.get(value);
            Comparable<Object> leftComparable = (Comparable<Object>)leftObj;
            //get right operand
            Field rightField = ReflectionUtils.findField(clazz, right);
            rightField.setAccessible(true);
            Object rightObj = rightField.get(value);
            
            //null check : return true if both null
            if(leftComparable == null) {
                if(rightObj == null) {
                    return true;
                } else {
                    context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
                        .addPropertyNode(left).addPropertyNode(right);
                    return false;
                }
            }
            
            //return true if compareTo result in vals array
            if(vals.contains(leftComparable.compareTo(rightObj))) {
                return true;
            }else {
                context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
                .addPropertyNode(left).addConstraintViolation();
                return false;
            }
        } catch (Exception e) {
            log.error("Cannot validate one of two field in '" + value + "'!", e);
            return false;
        }
    }
}


//Usage: selling price must be smallar than or equal to list price
@CompareFields(left = "sellingPrice", right = "listPrice", vals = {-1,0})
public class InventoryDto{
...
}
//Usage: start time must be before finish time
@CompareFields(left = "startTime", right = "finishTime", vals = {-1})
public class GateLogDto{
...
}
Carping answered 6/7, 2023 at 22:53 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Boorer

© 2022 - 2024 — McMap. All rights reserved.