java8 - 缺席变量&可选的

时间:2015-07-16 13:56:43

标签: java json jackson java-8

我正在解析输入JSON。对于一个领域,有三种可能性:

  • 该领域缺席;
  • 该值设置为null;
  • 该值设置为有效值。

实现了不同的行为:对于JSON中缺少的值,将默认值插入到数据库中;对于JSON中的空值,将空值插入数据库。

我考虑过Optional对此进行建模:

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

以下哪两项选项最有意义?

  1. 如果field为空,则JSON中不存在该字段。如果field是Optional.empty,则该字段在JSON中为null
  2. 如果field为空,则JSON中的字段为null。如果field是Optional.empty,则JSON中不存在该字段。
  3. FWIW,我使用Jackson和模块jackson-datatype-jdk8来解析输入JSON。

5 个答案:

答案 0 :(得分:5)

我认为你不应该在这种情况下使用Optional。正如@dkatzel在他的回答中提到的那样,它意味着比作为一个领域更多地被用作API返回值。

尽管有这样的学术讨论,但只需将Data类中的字段初始化为默认值即可完成您想要的任务:

public class Data {
    private String field = DEFAULT_VALUE;
}

然后让杰克逊做其余的事。

根据OP的评论编辑:

当您的JSON为null提供field值时,杰克逊会将其设置为null,以及将存储在数据库中的内容。

当您的JSON不包含该字段时,DEFAULT_VALUE将自动加载到您的Data实例中。

当你的JSON确实包含field的值时,杰克逊会设置它,并且该值将到达数据库。

编辑2,考虑到OP要求在解析JSON之后找出field是否填写,设置为null或输入JSON中是否缺席输入:

如果在解析输入JSON 之后,您需要知道field是填写,设置为null还是不存在,那么请考虑这个例子,这显示了我采取的方法:

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);
    }
}

我已使用private static属性来保存您的Data默认值和3种方法来查询任何字段状态(默认值,null或设置)。为了确定要查询的字段,这些方法会收到Function<Data, Object>,它们被赋予Data个实例并返回应该是所需字段的Object。 (如果您停下来思考它,可以将getter视为将实例作为输入并返回实例的特定字段的函数。)

所以稍后,当你需要知道某个字段如何到达你的JSON输入时,只需使用这3个查询方法就可以找到:

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

答案 1 :(得分:2)

我会说第一个选项具有最大的语义意义。它还可能允许更容易的计算。

如果java中的字段为null,则表示缺少与第一个选项匹配的值。

我建议您将这些字段存储在哈希映射中,其中键是JSON字段名称,值是JSON字段的值。我还建议你不要在这里使用一个可选项(因为它可以添加一个不必要的复杂层),而是在hashmap中使用null或非null对象。

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 :(得分:1)

第二种选择对我来说更有意义。 null表示null,空表示不存在。

但是,Optional不应该真正用作字段。 It's supposed to be used as an API return value

您是否可以将数据存储在允许空值的Map中?如果地图中没有关键字(您的字段),请返回Optional.empty

答案 3 :(得分:0)

既不?我会用@DefaultValue()注释我的POJO字段。然后,您的可能性是在JSON中指定的空值或非空值,或者如果从JSON中省略该字段,则为默认值。然后你就可以在没有任何特殊的每场分析的情况下坚持POJO。

答案 4 :(得分:0)

如果您正在处理Object而不是String,这是一个我觉得优雅的解决方案:

  • 如果没有值,则使用Optional.empty();
  • 如果有值
  • ,请使用Optional.of(value)
  • 如果值为null,则使用Optional.of(specialValue)

其中specialValue是一个静态单例,您可以轻松地进行测试,例如:ObjectUtils.NULL(来自 commons.lang )。

然后您可以轻松测试您的选项:

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 
}