Reputation: 1901
I have a REST web app java EE8 application.
Let's say I have this:
class PersonRequest {
String email;
String name;
String surname;
String mobile;
.. getter and setter
}
I want to create a resourche method that updates ONLY requested properties, therefore I use PATCH method.
A JSON request example:
PATCH
{
"mobile": "+3494441122" // update only mobile
}
Will modify only mobile field and leave other fields untouched.
THE ISSUE
How to deal with null values with PATCH method ?
Should I consider to turn blank the field or just ignore it ?
I'm worried about the the first case as I don't know if is there any way to recognize the difference, because either if I don't pass the field or i specify the null value, the Person's property field will be a null value.
Any hints?
Upvotes: 3
Views: 3501
Reputation: 40318
I'll go ahead and post a solution based on Java 8's Optional
. First of all the problem it is supposed to solve is mapping JSON undefined
values to Java for the HTTP PATCH case: null
in Java is inadequate as it might correspond to a null
or undefined
JSON value. And in the PATCH case we want to treat the 2 JSON values in a different way: null
actually sets to Java null
, undefined
leaves the value as is.
I tested it on WildFly 15, so I guess it will probably work on WildFly servers and anything else using RestEasy (e.g. Quarkus) - but I have not tested! I skimmed through the JAX-RS 2.1 specs and did not find any explicit mentioning of Optional
and how it should be handled, so beware this might be a RestEasy-only solution! Even worse, it might be related to the exact tool for handling the JSON, so could work e.g. with Jackson but not with JSON-B.
Also there is a question about correct style and using Optional
as intended; see the link in the comments of the question. Although I must admit this is NOT how I would normally construct a generic Java Bean, I believe this solution is good enough for this special case. Another solution I have tried in the past was keeping a boolean
variable alongside each normal variable (e.g. nameIsSet
). I think this and other solutions are in the end more cumbersome than the one outlined here.
The DTO:
class PersonPatchRequest {
Optional<String> email;
Optional<String> name;
Optional<String> surname;
Optional<String> mobile;
.. getter and setter for the Optional, e.g.:
public Optional<String> getName() {
return name;
}
public void setName(Optional<String> name) {
this.name = name;
}
}
Then sending the following object:
{
"name": "Bob",
"surname": null
}
E.g. as:
curl -X PATCH http://... -H "Accepts: application/json" -H "Content-Type: application/json" \
-d "{\"name\":\"Bob\",\"surname\":null}"
Will result in the following data in Java:
PersonPatchRequest {
email: null,
name: Optional["Bob"]
surname: Optional.empty
mobile: null
}
Optional.empty
values are the real null
s the client sent, null
values are the undefined
.
Upvotes: 1
Reputation: 849
Not the best solution but it should get you going.
import java.util.Iterator;
import org.json.JSONObject;
public class Test{
public static void main(String[] args) {
/** We will pass the Body of the request through a series of conditions listed below.
1. Check if the key exists,
2. Check if the value is not null,
3. Check if the value is not "null"
**/
JSONObject requestBody = new JSONObject("{\"mobile\":\"+3494441122\",\"email\":\"email\",\"name\":\"null\",\"surname\":null}");
JSONObject updateObject = new JSONObject();
Iterator<String> ittr = requestBody.keys();
while(ittr.hasNext()) {
String key = ittr.next();
if(requestBody.has(key)) {
if(!requestBody.isNull(key)) {
String value = requestBody.get(key).toString();
if(!value.equalsIgnoreCase("null")) {
//If these cases are matched, only then allow the value to be updated.
updateObject.put(key, value);
}
}
}
}
System.out.println(updateObject);
}
}
OUTPUT
{"mobile":"+3494441122","email":"email"}
You can also use GSON libary for doing so which is a better approach but it will accept "null" values unlike the previous approach.
Main Class
import org.json.JSONObject;
public class Test{
public static void main(String[] args) {
JSONObject requestBody = new JSONObject("{\"mobile\":\"+3494441122\",\"email\":\"email\",\"name\":\"null\",\"surname\":null}");
PersonRequest personRequest = new PersonRequest().fromJson(requestBody.toString() );
System.out.println(personRequest.toJson());
}
}
POJO Class
import org.json.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class PersonRequest {
String email;
String name;
String surname;
String mobile;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public JSONObject toJson() {
return new JSONObject(new Gson().toJson(this));
}
public PersonRequest fromJson(String json) {
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
return gson.fromJson(json, this.getClass());
}
}
Output :
{"name":"null","mobile":"+3494441122","email":"email"}
The difference is just that now name has a null String.
Upvotes: 1