Проверка JSR 303, если одно поле равно "что-то", то эти другие поля не должны быть null
Я хочу сделать небольшую пользовательскую проверку с помощью 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;
}
}
}
это panel.status.getValue();
дает мне неприятности.. не знаю, как это сделать.
5 ответов:
в этом случае я предлагаю написать пользовательский валидатор, который будет проверять на уровне класса (чтобы мы могли получить доступ к полям объекта), что одно поле требуется только в том случае, если другое поле имеет определенное значение. Обратите внимание, что вы должны написать универсальный валидатор, который получает 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(); } }
реализация валидатора
/** * 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; } }
пример использования валидатора
@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 }
обратите внимание, что реализация валидатора использует
BeanUtils
классcommons-beanutils
библиотеки, но вы также можете использоватьBeanWrapperImpl
от Spring Framework.см. Также этот великий ответ: перекрестная проверка поля с гибернацией Валидатор (JSR 303)
определить метод, который должен проверить значение true и поставить
@AssertTrue
Аннотация В верхней части:@AssertTrue private boolean isOk() { return someField != something || otherField != null; }
метод должен начинаться с "есть".
вы должны использовать пользовательские
DefaultGroupSequenceProvider<T>
:условная валидация.java
// Marker interface public interface ConditionalValidation {}
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; } }
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 }
см. также вопрос по этой теме.
другой подход заключается в создании (защищенный) геттер, который возвращает объект, содержащий все зависимые поля. Пример:
public class MyBean { protected String status; protected String name; @StatusAndSomethingValidator protected StatusAndSomething getStatusAndName() { return new StatusAndSomething(status,name); } }
StatusAndSomethingValidator теперь может получить доступ к StatusAndSomething.статус и StatusAndSomething.что-то и сделать зависимую проверку.
вот мой взгляд на это, старался держать его как можно проще.
интерфейс:
@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(); }
осуществление проверки:
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(); } }
использование:
@OneOf({"stateType", "modeType"}) public class OneOfValidatorTestClass { private StateType stateType; private ModeType modeType; }
сообщения:
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}