Sushil Kumar
Sushil Kumar

Reputation: 125

How to validate each object in the List in spring controller parameter without wrapping the List in other class?

I have a spring controller :

@RequestMapping(value = "bulk", method = RequestMethod.POST)
@ResponseBody
public APIResponse createBulkEnquiries(@Valid @RequestBody List<BulkDTO> bulkDTOs) {
    // some code 
}

It is not validating any of the bulkDTOs as @valid do not work on element of Collection directly (although BulkDTO is validatable). Also I can not wrap List in some other class (which works) like

public class ValidatableObjectsCollectionWrapper {
    @Valid
    List<BulkDTO> bulkDTOs;
}

because it will change input json format. So I need some other way around.

I also tried to make a custom validator for collection

public class CollectionValidator implements Validator {

private final SpringValidatorAdapter validator;

public CollectionValidator(SpringValidatorAdapter validator) {
    super();
    this.validator = validator;
}

@Override
public boolean supports(Class<?> clazz) {
    return Collection.class.equals(clazz);
}

@Override
public void validate(Object target, Errors errors) {

    Collection<Object> objectCollection = (Collection<Object>) target;

    for (Object object : objectCollection) {
        validator.validate(object, errors);
    }
}
}

But Don't know how to invoke or bind it on the controller param.

Upvotes: 4

Views: 8761

Answers (2)

abhinavsinghvirsen
abhinavsinghvirsen

Reputation: 2054

Actually, this can be achieved using Spring Validation and JSR303.

*Expose a MethodValidationPostProcessor bean.

Annotate your controller class with @Validated (org.springframework.validation.annotation.Validated)

Use the JSR303 validation annotations on your MyEntity fields/methods.

Annotate your RequestBody argument with @Valid (you've already done this in your example).

Add an @ExceptionHandler method to handle MethodArgumentNotValidException. This can be done in the controller or in a @ControllerAdvice class.*

Upvotes: 2

JB Nizet
JB Nizet

Reputation: 692171

Not sure if it's the only, or the best solution, but you can use a wrapper object, without having to change the JSON, using the @JsonValue and @JsonCreator annotations. Here is a complete example:

public class BulkDTOWrapper {

    private List<BulkDTO> bulks;

    @JsonCreator
    public BulkDTOWrapper(List<BulkDTO> bulks) {
        this.bulks = bulks;
    }

    public BulkDTOWrapper() {
    }

    @JsonValue
    public List<BulkDTO> getBulks() {
        return bulks;
    }

    public void setBulks(List<BulkDTO> bulks) {
        this.bulks = bulks;
    }

    public static void main(String[] args) throws IOException {
        BulkDTO b1 = new BulkDTO("hello");
        BulkDTO b2 = new BulkDTO("world");

        BulkDTOWrapper wrapper = new BulkDTOWrapper();
        wrapper.setBulks(Arrays.asList(b1, b2));

        ObjectMapper om = new ObjectMapper();
        String json = om.writeValueAsString(wrapper);
        System.out.println("json = " + json);

        BulkDTOWrapper wrapper2 = om.readValue(json, BulkDTOWrapper.class);
        System.out.println(wrapper2.getBulks().size());
    }
}

Upvotes: 6

Related Questions