validator example beans bean java validation bean-validation

beans - javax.validation example



Validación de JSR 303, si un campo es igual a "algo", entonces estos otros campos no deberían ser nulos (5)

Estoy buscando hacer una pequeña validación personalizada con JSR-303 javax.validation .

Tengo un campo Y si se ingresa un cierto valor en este campo, deseo solicitar que algunos otros campos no sean null .

Estoy tratando de resolver esto. No estoy seguro de cómo llamaría esto para encontrar una explicación.

Cualquier ayuda sería apreciada. Soy bastante nuevo en esto.

En este momento estoy pensando en una restricción personalizada. Pero no estoy seguro de cómo probar el valor del campo dependiente desde dentro de la anotación. Básicamente, no estoy seguro de cómo acceder al objeto del panel desde la anotación.

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; } } }

Es el panel.status.getValue(); me da problemas ... no estoy seguro de cómo lograr esto.


Aquí está mi opinión, intenté mantenerlo lo más simple posible.

La interfaz:

@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(); }

Implementación de validación:

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(); } }

Uso:

@OneOf({"stateType", "modeType"}) public class OneOfValidatorTestClass { private StateType stateType; private ModeType modeType; }

Mensajes:

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}


Debería hacer uso de DefaultGroupSequenceProvider<T> personalizado DefaultGroupSequenceProvider<T> :

ConditionalValidation.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 }

Ver también preguntas relacionadas sobre este tema .


Defina el método que debe validar como verdadero y coloque la anotación @AssertTrue en la parte superior:

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

El método debe comenzar con ''es''.


En este caso, sugiero que se escriba un validador personalizado, que validará a nivel de clase (para permitirnos acceder a los campos del objeto) que un campo sea necesario solo si otro campo tiene un valor particular. Tenga en cuenta que debe escribir un validador genérico que obtenga 2 nombres de campo y trabaje solo con estos 2 campos. Para requerir más de un campo, debe agregar este validador para cada campo.

Use el siguiente código como una idea (no lo he probado).

  • Interfaz de Validator

    /** * 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(); } }

  • Implementación del validador

    /** * 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; } }

  • Ejemplo de uso del validador

    @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 }

Tenga en cuenta que la implementación del validador usa la clase BeanUtils desde la biblioteca commons-beanutils , pero también podría usar BeanWrapperImpl desde Spring Framework .

Vea también esta gran respuesta: Validación de campo cruzado con Hibernate Validator (JSR 303)


Un enfoque diferente sería crear un getter (protegido) que devuelva un objeto que contenga todos los campos dependientes. Ejemplo:

public class MyBean { protected String status; protected String name; @StatusAndSomethingValidator protected StatusAndSomething getStatusAndName() { return new StatusAndSomething(status,name); } }

StatusAndSomethingValidator ahora puede acceder a StatusAndSomething.status y StatusAndSomething.something y hacer una verificación dependiente.