REST partial update using Custom Null Deserialiser for Json using Jackson

When working with partial update, especially with REST PATCH services, we should have encountered the problem of how to differentiate between whether an attribute has to set to null explicitly or ignore from updating (no need to update a field).

To give more insight on the topic,  let us see the following code snippet,

Location existingLocation = dao.getLocation(locationid);
merge(existingLocation,requestLocation);
dao.update(existingLocation);


The merge method looks like,




void merge(Location existing, LocationResource request){
   if(request.name != null) 
      existing.name = request.name;
   if(request.status != null)
     existing.status = request.status;
   if(request.type != null)
     existing.type = new.type;
   if(request.address != null)
     existing.address = request.address;
}


In case of merging the object, we should make sure we are only updating the existing location with requested location only for the attribute that is provided by the user, technically if the value is not null we will update.

The Location Object was like,

class Location {
  int locationId;
  String name;
  String status;
  String address;
  String type;
}

The challenge here is what if the user has to explicitly update an attribute to null?

Consider the following requests for partial update (/url/locationid),
Request 1:
{
  "status" : "close",
  "type" : "test"
}
Looks good, while executing the partial update for the above request, we first fetch the existing record and then overwrite just the status in the merge code.

Request 2:
{
  "status" : "close",
  "type" : "null"
}
This also looks good, while executing the partial update for the above request, 
the status is overwritten by new value & the value for type will be overwritten for first request as the condition becomes true ("null" != null)

Request 3:
{
  "status" : "close",
  "type" : null
}
Here comes the challenge request, while executing the partial update for the above request,
the status is overwritten by new value & the type will not be overwritten and we will ignore the field from updating explicitly to null.

It is very difficult to differentiate between whether user has explicitly set to null or he did not provide value for an attribute.

I was doing some research on this, one of the option was to provide a default value for each attribute. Instead of checking for null, check for default value in the merge code and update accordingly.

The solution was good, unfortunately we couldn't go with this solution as the Objects are auto generated from JSON schema and we do not want the default value to be exposed outside as part of JSON Schema.

Since we use RestEasy as our REST Implementation, we decided to write a value injector (RestEasy) where we used data binding using Jackson Deserialiser.
We didn't go for MessageReader approach as we need these only for few cases. The Deserialiser code looks like below,

 Null2NoneDeserializer deserializer = new Null2NoneDeserializer();

 SimpleModule module = new SimpleModule("Null2NoneDeserializer");
 module.addDeserializer(String.class, deserializer);

 ObjectMapper mapper = new ObjectMapper();
 mapper.registerModule(module);

 String json = httpreader.readFully();

 Location location = mapper.readValue(json, Location);

Null2NoneDeserializer.java

After adding the de-serializer the merge code will be as follows.
void merge(Location existing, LocationResource request){  
      
        if(request.name.equals(Null2NoneDeserializer. NULL_SUBSITUTE))
              existing.name =  null; //explicitly set to null
        else if(request.name != null)
              existing.name =  request.name;
...
...
}