JSR 303验证,如果一个字段等于"某事",那么这些其他字段不应为空

Eri*_*ric 72 java validation bean-validation

我想用JSR-303做一点自定义验证javax.validation.

我有一个领域.如果在此字段中输入了某个值,我想要求其他几个字段不是null.

我想弄清楚这一点.不确定我会称之为什么来帮助找到解释.

任何帮助,将不胜感激.我对此很新.

目前我正在考虑自定义约束.但我不确定如何从注释中测试依赖字段的值.基本上我不确定如何从注释访问面板对象.

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是panel.status.getValue();给我带来麻烦..不知道如何实现这一目标.

h22*_*h22 92

定义必须验证为true的方法并将@AssertTrue注释放在其顶部:

  @AssertTrue
  private boolean isOk() {
    return someField != something || otherField != null;
  }
Run Code Online (Sandbox Code Playgroud)

该方法必须以'is'开头.

  • 这是迄今为止最有效的选择.谢谢!@anaBad:该AssertTrue注释可以自定义消息,就像其他约束注释. (8认同)
  • 我不得不公开该方法 (2认同)

Sla*_*hin 89

在这种情况下,我建议编写自定义验证器,它将在类级别进行验证(以允许我们访问对象的字段),只有当另一个字段具有特定值时才需要一个字段.请注意,您应该编写通用验证器,它获取2个字段名称并仅使用这2个字段.要要求多个字段,您应该为每个字段添加此验证器.

使用以下代码作为一个想法(我没有测试它).

  • 验证器接口

    /**
     * Validates that field {@code dependFieldName} is not null if
     * field {@code fieldName} has value {@code fieldValue}.
     **/
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @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();
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 验证器实现

    /**
     * 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;
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 验证器用法示例

    @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
    }
    
    Run Code Online (Sandbox Code Playgroud)

请注意,验证器实现使用库中的BeanUtils类,commons-beanutils但您也可以使用BeanWrapperImplSpring Framework.

另请参阅这个很好的答案:使用Hibernate Validator进行交叉字段验证(JSR 303)

  • @Benedictus 这个**示例**仅适用于字符串,但您可以修改它以适用于任何对象。有两种方法:1)使用要验证的类(而不是“Object”)对验证器进行参数化。在这种情况下,您甚至不需要使用反射来获取值,但在这种情况下验证器变得不那么通用 2) 使用 Spring Framework(或其他库)中的“BeanWrapperImp”及其“getPropertyValue()”方法。在这种情况下,您将能够获取“Object”值并转换为您需要的任何类型。 (2认同)

use*_*153 15

你应该使用自定义DefaultGroupSequenceProvider<T>:

ConditionalValidation.java

// Marker interface
public interface ConditionalValidation {}
Run Code Online (Sandbox Code Playgroud)

MyCustomFormSequenceProvider.java

public class MyCustomFormSequenceProvider
    implements DefaultGroupSequenceProvider<MyCustomForm> {

    @Override
    public List<Class<?>> getValidationGroups(MyCustomForm myCustomForm) {

        List<Class<?>> sequence = new ArrayList<>();

        // Apply all validation rules from ConditionalValidation group
        // only if someField has given value
        if ("some value".equals(myCustomForm.getSomeField())) {
            sequence.add(ConditionalValidation.class);
        }

        // Apply all validation rules from default group
        sequence.add(MyCustomForm.class);

        return sequence;
    }
}
Run Code Online (Sandbox Code Playgroud)

MyCustomForm.java

@GroupSequenceProvider(MyCustomFormSequenceProvider.class)
public class MyCustomForm {

    private String someField;

    @NotEmpty(groups = ConditionalValidation.class)
    private String fieldTwo;

    @NotEmpty(groups = ConditionalValidation.class)
    private String fieldThree;

    @NotEmpty
    private String fieldAlwaysValidated;


    // getters, setters omitted
}
Run Code Online (Sandbox Code Playgroud)

另请参阅有关此主题的相关问题.

  • @user238607 getValidationGroups(MyCustomForm myCustomForm) 将在每个 bean 实例中调用多次,并且有时会传递 null。如果它通过null,您只需忽略。 (2认同)

jok*_*arl 6

这是我的看法,试图使其尽可能简单。

界面:

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = OneOfValidator.class)
@Documented
public @interface OneOf {

    String message() default "{one.of.message}";

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

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

    String[] value();
}
Run Code Online (Sandbox Code Playgroud)

验证实施:

public class OneOfValidator implements ConstraintValidator<OneOf, Object> {

    private String[] fields;

    @Override
    public void initialize(OneOf annotation) {
        this.fields = annotation.value();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {

        BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);

        int matches = countNumberOfMatches(wrapper);

        if (matches > 1) {
            setValidationErrorMessage(context, "one.of.too.many.matches.message");
            return false;
        } else if (matches == 0) {
            setValidationErrorMessage(context, "one.of.no.matches.message");
            return false;
        }

        return true;
    }

    private int countNumberOfMatches(BeanWrapper wrapper) {
        int matches = 0;
        for (String field : fields) {
            Object value = wrapper.getPropertyValue(field);
            boolean isPresent = detectOptionalValue(value);

            if (value != null && isPresent) {
                matches++;
            }
        }
        return matches;
    }

    private boolean detectOptionalValue(Object value) {
        if (value instanceof Optional) {
            return ((Optional) value).isPresent();
        }
        return true;
    }

    private void setValidationErrorMessage(ConstraintValidatorContext context, String template) {
        context.disableDefaultConstraintViolation();
        context
            .buildConstraintViolationWithTemplate("{" + template + "}")
            .addConstraintViolation();
    }

}
Run Code Online (Sandbox Code Playgroud)

用法:

@OneOf({"stateType", "modeType"})
public class OneOfValidatorTestClass {

    private StateType stateType;

    private ModeType modeType;

}
Run Code Online (Sandbox Code Playgroud)

讯息:

one.of.too.many.matches.message=Only one of the following fields can be specified: {value}
one.of.no.matches.message=Exactly one of the following fields must be specified: {value}
Run Code Online (Sandbox Code Playgroud)