简体   繁体   English

java8 - 缺少变量和可选

[英]java8 - absent variable & Optional

I am parsing input JSON. 我正在解析输入JSON。 For a field, there are 3 possibilities: 对于一个领域,有三种可能性:

  • the field is absent; 这个领域缺席;
  • the value is set to null; 该值设置为null;
  • the value is set to something valid. 该值设置为有效的值。

Different behavior is implemented: for an absent value in the JSON, the default value is inserted into the database; 实现了不同的行为:对于JSON中缺少的值,将默认值插入到数据库中; for a null value in the JSON, a null value is inserted into the database. 对于JSON中的空值,将空值插入到数据库中。

I thought about Optional to model this: 我想到了Optional模型:

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

Which of the following two options make most sense? 以下哪两个选项最有意义?

  1. If field is null, the field was absent in the JSON. 如果field为null,则JSON中不存在该字段。 If field is Optional.empty, the field is null in the JSON. 如果field为Optional.empty,则JSON中的字段为null
  2. If field is null, the field was null in the JSON. 如果field为null,则JSON中的字段为null If field is Optional.empty, the field is absent in the JSON. 如果field为Optional.empty,则JSON中不存在该字段。

FWIW, I am using Jackson with module jackson-datatype-jdk8 to parse the input JSON. FWIW,我使用Jackson和模块jackson-datatype-jdk8来解析输入JSON。

I think you shouldn't use Optional for this scenario. 我认为你不应该在这种情况下使用Optional As @dkatzel has mentioned in his answer, it's meant to be used as an API return value more than as a field. 正如@dkatzel在他的回答中所提到的,它意味着比作为字段更多地用作API返回值。

Despite this academic discussion, you can accomplish what you want simply by initializing fields in your Data class to their default values: 尽管进行了这方面的学术讨论,但只需将Data类中的字段初始化为默认值即可实现您的目标:

public class Data {
    private String field = DEFAULT_VALUE;
}

And then let Jackson do the rest. 然后让杰克逊做其余的事情。


EDIT as per OP's comment: 根据OP的评论编辑:

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. 当您的JSON为field提供null值时,Jackson会将其设置为null ,这将存储在数据库中。

When your JSON does not contain the field, the DEFAULT_VALUE will be automatically loaded in your Data instance. 当您的JSON不包含该字段时, DEFAULT_VALUE将自动加载到您的Data实例中。

And when your JSON does actually contain a value for the field , Jackson will set it, and that value will reach the database. 当你的JSON确实包含该field的值时,Jackson将设置它,并且该值将到达数据库。


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: 编辑2,考虑到OP要求在解析JSON输入后找出field是填入,设置为null还是在输入JSON中不存在的要求:

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: 如果在解析输入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);
    }
}

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). 在这里,我使用了一个private static属性来保存Data的默认值和3个方法来查询任何字段状态(默认值,null或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. 为了确定要查询的字段,这些方法接收一个Function<Data, Object> ,它们被赋予一个Data实例并返回一个应该是所需字段的Object (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). (如果你停下来想一想,可以将getter视为将实例作为输入并返回实例的特定字段的函数)。

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: 所以稍后,当你需要知道某个字段如何到达你的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

I would say that the first option makes the most semantic sense. 我会说第一个选项具有最大的语义意义。 It also potentially allows for easier computation. 它还可能允许更容易的计算。

Where a field in java is null, it is implied that a value is missing, which matches the first option. 如果java中的字段为null,则表示缺少与第一个选项匹配的值。

I suggest that you store these fields in a hash-map where the key is the JSON field name and the value is the JSON field's value. 我建议您将这些字段存储在哈希映射中,其中键是JSON字段名称,值是JSON字段的值。 I also suggest you don't use an optional here (as it can add an unnecessary layer of complexity), and instead use either a null or non-null object in the hashmap. 我还建议你不要在这里使用一个可选项(因为它可以添加一个不必要的复杂层),而是在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;
}

The second choice makes more sense to me. 第二种选择对我来说更有意义。 null means null and empty means not present. null表示null ,empty表示不存在。

However, Optional shouldn't really be used as a field. 但是, Optional不应该真正用作字段。 It's supposed to be used as an API return value . 它应该被用作API返回值

Could you instead store the data in a Map that allows null values? 您可以将数据存储在允许空值的Map中吗? And if the key (your field) isn't present in the map, then return Optional.empty ? 如果地图中没有关键字(您的字段),则返回Optional.empty

Neither? 都不是? I would annotate my POJO fields with @DefaultValue(). 我会用@DefaultValue()注释我的POJO字段。 Then your possibilities are a null value or a non-null value specified in JSON, or the default if the field was omitted from JSON. 然后,您的可能性是在JSON中指定的空值或非空值,或者如果从JSON中省略该字段,则为默认值。 And you can then just persist the POJO without any special per-field analysis. 然后,您可以在没有任何特殊的每场分析的情况下坚持POJO。

If you are dealing with Object instead of String , here's a solution I find elegant: 如果您正在处理Object而不是String ,那么这是一个我觉得优雅的解决方案:

  • use Optional.empty(); 使用Optional.empty(); if there is no value 如果没有价值
  • use Optional.of(value) if there is a value 如果有Optional.of(value)使用Optional.of(value)
  • use Optional.of(specialValue) if the value is null 如果值为null,请使用Optional.of(specialValue)

where specialValue is a static singleton you can easily test, for instance: ObjectUtils.NULL (from commons.lang ). 其中specialValue是一个静态单例,你可以轻松测试,例如: ObjectUtils.NULL (来自commons.lang )。

Then you can easily test your optional: 然后你可以轻松测试你的可选:

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 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM