views:

61

answers:

3

I've a form I want to validate. It contains 2 Address variables. address1 has always to be validated, address2 has to be validated based on some conditions

public class MyForm {
    String name;
    @Valid Address address1;
    Address address2;
 }

public class Address {
    @NotEmpty   
    private String street;
}

my controller automatically validates and binds my form obj

@RequestMapping(...)
public ModelAndView edit(
        @ModelAttribute("form")
        @Valid
        MyForm form,
        BindingResult bindingResult,
        ...)

        if(someCondition) {
            VALIDATE form.address2 USING JSR 303

the problem is that if I use the LocalValidatorFactoryBean validator i can't reuse the BinidingResult object provided by Spring. The bind won't work as the target object of 'result' is 'MyForm' and not 'Address'

validate(form.getAddress2(), bindingResult)   //won't work

I'm wondering what's the standard/clean approach to do conditional validation.

I was thinking in programmatically create a new BindingResult in my controller.

final BindingResult bindingResultAddress2 = new BeanPropertyBindingResult(address2, "form");
validate(form.getAddress2(), bindingResultAddress2);

but then the List of errors I obtain from bindingResultAddress2 can't be added to the general 'bindingResult' as the field names are not correct ('street' instead of 'address2.street') and the binding won't work.

Some dirty approach would be to extend BeanPropertyBindingResult to accept some string to append to the fields name.. do you have a better approach?

+1  A: 

The standard approach for validating hierarchical structures is to use pushNestedPath()/popNestedPath(), though I'm not sure how it plays with JSR-303:

bindingResult.pushNestedPath("address2");
validate(form.getAddress2(), bindingResult);
bindingResult.popNestedPath();
axtavt
Thanks axtavt, it works. I'm wondering if this is the best approach to make it, why there's no groups attribute in @Valid and if JSR-303 groups concept is the only way to achieve conditional validation
al nik
There is a feature request for groups in `@Valid`: https://jira.springframework.org/browse/SPR-6373
axtavt
very helpful, thanks
al nik
A: 

I've never tried myself, but I think the correct approach is using validator groups.

Sinuhe
Unfortunately @Valid has no groups
al nik
A: 

First of all, let's see @javax.validation.Valid API

Mark an association as cascaded. The associated object will be validated by cascade.

When Spring framework uses @Valid as a marker to validate its command objects, it corrupts its purpose. Spring should instead create your own specific annotation which specifies the groups which should be validated.

Unfortunately, you should use Spring native Validator API if you need to validate some groups

public void doSomething(Command command, Errors errors) {
    new BeanValidationValidator(SomeUserCase.class, OtherUserCase.class)
        .validate(command, errors);

    if(errors.hasErrors()) {

    } else {

    }
}

BeanValidationValidator can be implemented as

public class BeanValidationValidator implements Validator {

    javax.validation.Validator validator = ValidatorUtil.getValidator();

    private Class [] groups;

    public BeanValidationValidator(Class... groups) {
        this.groups = groups;
    }

    public void validate(Object command, Errors errors) {
        Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(command, groups);

        for(ConstraintViolation<Object> constraintViolation: constraintViolationSet) {
            errors.rejectValue(constraintViolation.getPropertyPath().toString(), null, constraintViolation.getMessage()); 
        }
    }

}
Arthur Ronald F D Garcia