Spring MVC REST using @RequestBody List <?> Returns HTTP 400 syntactically incorrect
I am using Spring 4 + Jackson 2 and wrote a fully functional POST method using @RequestBody in a custom class. This method has no problem with dismantling the object.
@ResponseBody
@RequestMapping(value="store", method = RequestMethod.POST)
public ServiceResponse store(@RequestBody CustomClass list) {
...
}
// Request: { code: "A", amount: 200 }
When I tried to add another method to handle a collection of the same class, my POST requests were returning with the following error.
HTTP Status 400: The request sent by the client was syntactically incorrect.
I note that this error usually occurs when the JSON provided does not match the entity class. However, all I do is represent an array of the same object instead of the object itself, which has already proven to work.
@ResponseBody
@RequestMapping(value="store-bulk", method = RequestMethod.POST)
public ServiceResponse storeBulk(@RequestBody List<CustomClass> list) {
...
}
// Request: [{ code: "A", amount: 200 }, { code: "B", amount: 400 }]
Did I miss something?
source to share
The answer is that Spring 4 doesn't actually get rid of type erasure, unlike what some other solutions suggest. While experimenting with debugging with manual unmarshalling, I decided to just handle this step myself, instead of an implicit cast that I have no control over. I hope someone comes along and proves that I am wrong by demonstrating a more intuitive solution though.
@ResponseBody
@RequestMapping(value="store-bulk", method = RequestMethod.POST)
public ServiceResponse storeBulk(@RequestBody String json) {
try {
List<CustomClass> list = new ObjectMapper().readValue(json, new TypeReference<List<CustomClass>>() { });
...
} catch (Exception e) {
...
}
}
Bonus: right after I got this working, I ran into this exception:
IllegalStateException: There was already a POJO for id
If anyone gets this, it is because the objects in the list are referencing some kind of object that is already referencing another item in the list. I could work around this as this object was identical across my entire collection, so I just removed the JSON-side reference from all but the first object. Then I added the missing links after the JSON was unarmored into the List object.
Two layers for Java 8 users (user object reference was the problem in my case):
User user = list.get(0).getUser();
list.stream().filter(c -> c.getUser() == null).forEach(t -> t.setUser(user));
source to share
In Java, type information for generics is erased at runtime, so Spring sees yours List<CustomClass> object
as List<Object> object
, so it can't figure out how to parse it.
One way to solve it is to grab the type information by creating a wrapper class for your list, for example:
public class CustomClassList extends ArrayList<CustomClass> {
}
source to share