I have a similar case that is working in Java (you can rewrite to Kotlin). Please try :
define a new bean in your configuration class annotated with @Configuration :
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
annotate your controller with :
@RestController
@Validated
Then you can validate you class with :
postHours(@RequestBody LinkedHashMap<String, @Valid Hours> hours)
----- MODIFICATION TO PROVIDE FULL EXAMPLE -----
You can use this full example. I am using : spring-boot-starter-web 2.2.6 with lombok.
Here is the class to validate :
@Data
public class Foo {
@Max(value = 3)
private Integer count;
}
Controller :
@RestController
@Validated
public class FooController {
@PostMapping("/validate")
public String validate(@RequestBody LinkedHashMap<String, List<@Valid Foo>> foos) {
return "foo";
}
}
Configuration class :
@Configuration
public class AppConfig {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
Controller advice (to send exception as json) :
@ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@Data
@AllArgsConstructor
class ApiError {
private HttpStatus status;
private String message;
private List<String> errors;
}
@ExceptionHandler({ ConstraintViolationException.class })
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
List<String> errors = new ArrayList<>();
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
errors.add(violation.getRootBeanClass().getName() + " " +
violation.getPropertyPath() + ": " + violation.getMessage());
}
ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
}
}
Test class :
@WebMvcTest(FooController.class)
public class FooControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void test() throws Exception {
LinkedHashMap<String, Foo> foos = new LinkedHashMap<>();
Foo foo = new Foo();
foo.setCount(4);
foos.put("foo", foo);
this.mockMvc.perform(post("/validate")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(bars)))
.andExpect(status().isBadRequest())
.andExpect(content().string(containsString("validate.foos[foo].<map value>[0].count: must be less than or equal to 3")));
}
}
The unit test shows that validation inside linkedhashmap is working.