As far as I know, Bean validation groups are not designed to meet this use case.
If you call your REST service like that :
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET -d '{"a1":{}, "a2":{}}}' http://localhost:8080/abc
The response is :
{ "timestamp":"2017-11-10T17:22:07.534+0000", "status":400, "error":"Bad Request", "exception":"org.springframework.web.bind.MethodArgumentNotValidException", "errors":[ { "codes":["NotNull.b.a1.name", "NotNull.a1.name", "NotNull.name", "NotNull.java.lang.String", "NotNull"], "arguments":[ { "codes":["b.a1.name", "a1.name" ], "arguments":null, "defaultMessage":"a1.name", "code":"a1.name" } ], "defaultMessage":"may not be null", "objectName":"b", "field":"a1.name", "rejectedValue":null, "bindingFailure":false, "code":"NotNull" }, { "codes":["NotNull.b.a2.age", "NotNull.a2.age", "NotNull.age", "NotNull.java.lang.String", "NotNull"], "arguments":[ { "codes":[ "b.a2.age", "a2.age"], "arguments":null, "defaultMessage":"a2.age", "code":"a2.age" } ], "defaultMessage":"may not be null", "objectName":"b", "field":"a2.age", "rejectedValue":null, "bindingFailure":false, "code":"NotNull" }, { "codes":[ "NotNull.b.a1.age", "NotNull.a1.age", "NotNull.age", "NotNull.java.lang.String", "NotNull"], "arguments":[ { "codes":["b.a1.age", "a1.age"], "arguments":null, "defaultMessage":"a1.age", "code":"a1.age" } ], "defaultMessage":"may not be null", "objectName":"b", "field":"a1.age", "rejectedValue":null, "bindingFailure":false, "code":"NotNull" }, { "codes":["NotNull.b.a2.name", "NotNull.a2.name", "NotNull.name", "NotNull.java.lang.String", "NotNull"], "arguments":[ { "codes":["b.a2.name", "a2.name"], "arguments":null, "defaultMessage":"a2.name", "code":"a2.name" } ], "defaultMessage":"may not be null", "objectName":"b", "field":"a2.name", "rejectedValue":null, "bindingFailure":false, "code":"NotNull" } ], "message":"Validation failed for object='b'. Error count: 4", "path":"/api/profile/abc" }
If you look closely you'll see that the response says that you get four validation errors and, for each one, validation details like the constraint, object and targeted field, for example : NotNull.b.a1.name.
It's the responsability of the view (REST client) to interpolate this detailled response to display the appropriate validation messages.
Sometimes it's not an option to interpolate messages on the client side. In this case you can add a custom "validation errors" handler with Spring. Something like that :
@RestController public class Controller1 { // This assumes that Spring i18n is properly configured @Autowired private MessageSource messageSource; @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseBody public Map<String, String> processValidationError(MethodArgumentNotValidException ex) { BindingResult result = ex.getBindingResult(); List<FieldError> fieldErrors = result.getFieldErrors(); Map<String, String> errors = new HashMap<>(); for (FieldError fieldError: fieldErrors) { String fieldPath = fieldError.getField(); String messageCode = fieldError.getCode() + "." + fieldPath; String validationMessage = messageSource.getMessage(messageCode, new Object[]{fieldError.getRejectedValue()}, Locale.getDefault()); // add the validation message, for example "NotNull.a1.name" => "a should not be null" errors.put(fieldError.getField(), validationMessage); } return errors; } @GetMapping(value="/abc") public void api1(@Valid @RequestBody B b){ //... } }