I think you shouldn't use Optional
for this scenario. As @dkatzel has mentioned in his answer, it's meant to be used as an API return value more than as a field.
Despite this academic discussion, you can accomplish what you want simply by initializing fields in your Data
class to their default values:
public class Data {
private String field = DEFAULT_VALUE;
}
And then let Jackson do the rest.
EDIT as per OP's comment:
When your JSON comes with a null
value for the field
, Jackson will set it to null
, and that's what will be stored in the database.
When your JSON does not contain the field, the DEFAULT_VALUE
will be automatically loaded in your Data
instance.
And when your JSON does actually contain a value for the field
, Jackson will set it, and that value will reach the database.
EDIT 2, considering OP's requirement to find out if the field
was either filled in, set to null
or was absent in the input JSON, after parsing the JSON input:
If, after parsing the input JSON, you need to know whether the field
was either filled in, set to null
or was absent, then consider this example, which shows the approach I'd take:
public class Data {
private String field1 = "hello";
private Integer field2 = 10;
private Double field3 = 3.75;
private static final Data DEFAULTS = new Data(); // defaults will be kept here
public String getField1() {
return this.field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public Integer getField2() {
return this.field2;
}
public void setField2(Integer field2) {
this.field2 = field2;
}
public Double getField3() {
return this.field3;
}
public void setField3(Double field3) {
this.field3 = field3;
}
@Override
public String toString() {
return "Data [field1=" + this.field1 +
", field2=" + this.field2 +
", field3=" + this.field3 + "]";
}
public boolean isDefault(Function<Data, Object> getter) {
Object defaultProperty = getter.apply(DEFAULTS);
Object actualProperty = getter.apply(this);
return defaultProperty != null // needed to support fields with no default value
&& defaultProperty.equals(actualProperty);
}
public boolean isNull(Function<Data, Object> getter) {
return getter.apply(this) == null;
}
public boolean isSet(Function<Data, Object> getter) {
return !this.isNull(getter) && !this.isDefault(getter);
}
}
Here I've used a private static
attribute to hold your Data
's default values and 3 methods to query any field state (default, null or set). In order to determine which field to query, these methods receive a Function<Data, Object>
, which are given a Data
instance and return an Object
that is supposed to be the desired field. (If you stop to think it, getters can be seen as functions that take the instance as input and return a specific field of the instance).
So later, when you need to know how a certain field arrived in your JSON input, just use those 3 query methods to find out:
ObjectMapper m = new ObjectMapper();
String json = "{\"field1\":null,\"field2\":20}";
Data data = m.readValue(json, Data.class);
System.out.println(data); // Data [field1=null, field2=20, field3=3.75]
System.out.println("field1 default ? " + data.isDefault(Data::getField1)); // false
System.out.println("field1 null ? " + data.isNull(Data::getField1)); // true
System.out.println("field1 set ? " + data.isSet(Data::getField1)); // false
System.out.println("field2 default ? " + data.isDefault(Data::getField2)); // false
System.out.println("field2 null ? " + data.isNull(Data::getField2)); // false
System.out.println("field2 set ? " + data.isSet(Data::getField2)); // true
System.out.println("field3 default ? " + data.isDefault(Data::getField3)); // true
System.out.println("field3 null ? " + data.isNull(Data::getField3)); // false
System.out.println("field3 set ? " + data.isSet(Data::getField3)); // false