Java8 - missing variable & Advanced

I am parsing JSON input. There are 3 possibilities for the field:

  • the field is missing
  • the value is null;
  • the value is set to something real.

Various behaviors are performed: for a missing value in JSON, the default value is inserted into the database; for a null value in JSON, a null value is inserted into the database.

I thought of Optional to simulate this:

 public class Data { private Optional<String> field; } 

Which of the following two options makes sense?

  • If field is null, the field is not in JSON. If field optional. Empty, this field is null in JSON.
  • If field is null, the field was null in JSON. If field optional. Empty, this field is not in JSON.

FWIW, I am using Jackson with the jackson-datatype-jdk8 module to parse JSON input.

+6
source share
5 answers

I think you should not use Optional for this scenario. As @dkatzel noted in his answer, this meant that it was used as an API return value more than as a field.

Despite this academic discussion, you can accomplish what you want by simply initializing the fields in the Data class with default values:

 public class Data { private String field = DEFAULT_VALUE; } 

And then let Jackson do the rest.


EDIT as per OP comment:

When your JSON is null for field , Jackson will set it to null , and that will be stored in the database.

If your JSON does not contain a field, DEFAULT_VALUE will be automatically loaded into your Data instance.

And when your JSON really contains a value for field , Jackson will set it, and that value will reach the database.


EDIT 2, given the OP requirement, to find out if a field populated, set to null or not in the input JSON after parsing the JSON input:

If, after parsing the input JSON, you need to know if the field filled, set to null or absent, then consider this example, which shows the approach that I will 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 used the private static attribute to store your default Data values ​​and 3 methods to query any field state (default, null or set). To determine which field to request, these methods get Function<Data, Object> , which are assigned an instance of Data and returns an Object , which should be desired. (If you stop thinking about it, then getters can be thought of as functions that take an instance as input and return a specific instance field.)

So, when you need to know how a specific field arrived in your JSON input, just use these 3 request 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 
+4
source

I would say that the first option makes the most sense. It also has the potential to simplify the calculation.

If the field in java is null, it is assumed that the value is absent, which corresponds to the first option.

I suggest storing these fields in a hash map, where the key is the name of the JSON field and the value is the value of the JSON field. I also suggest you not use the optional here (since it can add an unnecessary level of complexity), but instead use either a null or a null object in the hash map.

 HashMap<String, Value> jsonFields = new HashMap<String, Value>(); boolean hasField1 = false; Value field1Value = null; if(jsonFields.contains("field1"){ // It is present in the JSON file field1Value = jsonFields.get("field1"); // "null" here would mean that the JSON field was set to "null" hasField1 = true; } 
+2
source

The second choice makes more sense to me. null means null , and empty does not.

However, Optional should not be used as a field. It should be used as an API return value .

Could you save the data in a Map that allows null values? And if the key (your field) is missing on the map, return Optional.empty ?

+1
source

None? I would like to annotate POJO fields using @DefaultValue (). Then your possibilities are a null value or a nonzero value specified in JSON, or a default value if the field was omitted from JSON. And you can just save the POJO without any special analysis in the field.

0
source

If you are dealing with Object instead of String , here is a solution that I find elegant:

  • use Optional.empty(); if no value
  • use Optional.of(value) if there is a value
  • use Optional.of(specialValue) if the value is null

where specialValue is a static singleton that you can easily check, for example: ObjectUtils.NULL (from commons.lang).

Then you can easily test your option:

 if (optional.isPresent()) { if (ObjectUtils.NULL.equals(optional.get())) { // value is there and null } else { // value is there and not null } } else { // value is not there } 
0
source

All Articles