2

I want to merge the REST PATH payload to the 'Entity' object after getting it from a database, so that only the attributes provided in payload will be updated in entity. Hence, I want to ensure that only the attributes provided as part of patch payload will be updated safely.

I am using Spring Rest Controller with Hibernate entities.

@PatchMapping(value = "/{id}")
public Resource<DepartmentPEO> update(@PathVariable Long id,
        @RequestBody JSONObject payload) throws Exception
{
    DepartmentPEO eo = departmentService.getRow(id);
    // Have to do something to update the eo object from jsonObject.
    // Some api to update eo
    eo = departmentService.update(id, eo);

    Resource<DepartmentPEO> resource = new Resource<>(eo);
    DepartmentPEO dept = resource.getContent();
    id = dept.getDeptSeq();
    resource.add(
            linkTo(methodOn(DepartmentsRestController.class).getRow(id))
                    .withSelfRel());
    return resource;
}

Only the modified attributes will be sent as part of payload to server instead of sending all attributes.Resource(entity) will have nested list of objects (One-to-many). Am looking for the pool-proof solution for this use case and also believe this is common/basic for every rest api supported apps.

Pointing to any API to solve this would greatly appreciated!

Thank you

1 Answers1

0

Here is a working example using Jackson's ObjectMapper and BeanUtils from Spring (since I assume you're using Spring) :

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.beans.BeanUtils;

public class StackOverflow {

 @Test
 public void mergeTest() throws IOException, JSONException {
    DepartmentPEO existingDepartement = existingDepartmentPEO();

    JSONObject payload = new JSONObject();
    payload.put("name", "newName");
    DepartmentPEO result = mergeToObject(payload, existingDepartement);
    assert result.getName().equals("newName");
    assert result.getId().equals("1");
 }

 private DepartmentPEO existingDepartmentPEO() {
    DepartmentPEO existingDepartement = new DepartmentPEO();
    existingDepartement.setId("1");
    existingDepartement.setName("oldName");
    return existingDepartement;
 }

 private DepartmentPEO mergeToObject(JSONObject payload, DepartmentPEO object) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    DepartmentPEO updateRequest = objectMapper.readValue(payload.toString(), DepartmentPEO.class);
    BeanUtils.copyProperties(updateRequest, object, "id");
    return object;
 }
}

Here, I transform the JSONObject into a DepartmentPEO class then I copy this object into the existing one ignoring the field id.

You may want to have a generic way to ignore null fields from the JSONObject, then you can refer to this post for instance How to ignore null values using springframework BeanUtils copyProperties?

I would advice to send directly the DepartmentPEO object into the REST method signature instead of using a JSONObject.

Regards

user3793803
  • 336
  • 4
  • 7
  • Thank you user3793803, I see one issue with this approach i.e What happen if user tries to nullify the value via payload? attribute will not be nullified as we are ignorinig, do you have any solution for this? – dhorrairaajj Nov 06 '18 at 17:04
  • Then you will have to add extrat behaviour like differenciate explicit null value from no value from the JSON you received. You can check for explicit null value before creating the object from the Json. Like putting them into an array. And then soustract them from the properties array. But this could become hard to maintain and tedious if you have nested objects ... If you have complexe update policy I would recommend to refer to this article for proper PATCH request : https://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/ – user3793803 Nov 06 '18 at 18:11
  • Thank you user3793803 for pointing the doc, My intention is same to send only the modified attributes in the payload. Reason to use JSONObject is to findout the null values before updating to target object. Yes, resource(entity) will have nested list of objects (One-to-many). Am looking for the pool-proof solution for this use case and also believe this is common/basic for every rest api supported apps. thank you for your time spending on this. Appreciate any further api pointers to resolve this. – dhorrairaajj Nov 08 '18 at 06:36