i have a PATCH mapping method that currently accepts a Map<String, Object>
as @ResponseBody
, because i’ve seen ReflectionUtils
as one of ways to implement the PatchMapping, but when I want to pass for example String
, Integer
and Long
, then JSON doesn’t distinguish which one are which one. I would like to be able to pass EntityID which is Long, some Value which is Integer and some Strings at once.
This is my current Controller method
@PatchMapping("/grades/{id}")
public GradeEto partialUpdate(@PathVariable("id") final Long id, @RequestBody Map<String, Object> updateInfo) {
return gradeService.partialUpdate(id, updateInfo);
}
This is main part of the ReflectionUtils
update
@Override
public GradeEto partialUpdate(Long id, Map<String, Object> updateInfo) {
Grade grade = gradeRepository.findById(id).get();
GradeEto gradeeto = GradeMapper.mapToETO(grade);
updateInfo.forEach((key, value) -> {
Field field = ReflectionUtils.findField(GradeEto.class, key);
field.setAccessible(true);
ReflectionUtils.setField(field, gradeeto, value);
});
And i want to be able to pass for example
{
"value": 2, <-- Integer
"comment": "Test Idea", <-- String
"subjectEntityId": 2 <-- Long
}
But it gives me IllegalArgumentException
currently
How should i do it? And if im doing it the wrong way then how should I do it?
2
Answers
You can make use of the functionality offered by Jackson to perform the partial update.
For that, instead of reading the data to patch as a
Map
you specify in your Controller the type of the request body asObjectNode
:Then inside the Service to operate with
ObjectNode
s you need to turnGradeEto
instance into a node tree usingObjectMapper.valueToTree()
(assuming that Service class is a managed Bean you can injectObjectMapper
into it instead of instantiating it locally).Method
ObjectNode.fields()
can be used to iterate over the fields and corresponding values ofupdateInfo
. MethodsisNull()
andisEmpty()
, whichObjectNode
derives from its super types, would be handy to check if a particular property needs to be updated.Then, finally, you would need to parse the
ObjectNode
back intoGradeEto
type.So the code in the Service might look like this:
Note: it’s worth to draw the reader’s attention to the fact we also need to update the information in the Database. This logic is missing in both the Question and this Answer, since it’s not relevant to the narrow technical problem of extracting the data that should be patched from JSON, but we definitely need to save the patched object (for instance a method that produces patched object can be wrapped with another method responsible for retrieving/updating the data in the database).
I do believe
org.springframework.cglib.beans.BeanMap
can solve your problem in elegant way: