什么时候JsonNode实例为null?

时间:2017-11-25 06:04:24

标签: java jackson

考虑以下Java类定义。

CrudOperation.java:

package com.cyberfront.test.json.nll.demonstration;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.JsonNode;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CrudOperation{
    public enum Type {CREATE,UPDATE,DELETE,READ}
    private final Type type;
    private final JsonNode op;

    public CrudOperation(Type type) { this(type, null); }
    public CrudOperation(JsonNode op) { this(Type.UPDATE, op); }

    @JsonCreator
    public CrudOperation(@JsonProperty("type") Type type, @JsonProperty("op") JsonNode op) {
        this.type = type;
        this.op = op;

        boolean isUpdate = Type.UPDATE.equals(this.getType());
        boolean isNotNull = null == this.getOp();
        boolean isValid = isUpdate ^ isNotNull;

        if (!isValid) {
            System.out.println(" isUpdate: " + String.valueOf(isUpdate));
            System.out.println("isNotNull: " + String.valueOf(isNotNull));
            System.out.println("  isValid: " + String.valueOf(isValid));

            throw new IllegalArgumentException("Operation Failed Validation: " + this.toString());
        }
    }

    @JsonProperty("type")
    public Type getType() { return this.type; };

    @JsonProperty("op")
    public JsonNode getOp() { return this.op; }

    public static <T> String nullCheck(T val) { return null == val ? "null" : val.toString(); }

    public static String toString(Type type, JsonNode op) {
        return "{\"type\":\"" + nullCheck(type) + "\",\"op\":" + nullCheck(op) + "}";
    }

    @Override
    public String toString() { return toString(this.getType(), this.getOp()); }
}

在此类中,带有@JsonCreator注释的构造函数执行验证,以确保在JsonNode参数具有值时确实存在非空op参数type Type.UPDATE。也就是说,当type的值为Type.UPDATE时,op应为非空。如果type具有任何其他值,则op应为null。

接下来考虑下面的课程。

Main.java:

package com.cyberfront.test.json.nll.demonstration;

import com.cyberfront.test.json.nll.demonstration.CrudOperation.Type;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {

    private static ObjectMapper mapper = new ObjectMapper();

    public static void main(String[] args) {
        CrudOperation createObject = new CrudOperation(Type.CREATE);
        System.out.println("Initial value: " + createObject.toString());

        JsonNode createDocument = mapper.valueToTree(createObject);
        System.out.println("Document value: " + createDocument.toString());

        CrudOperation reconstructedObject = null;
        try {
            reconstructedObject = mapper.treeToValue(createDocument, CrudOperation.class);
        } catch (JsonProcessingException e) {
            System.out.println(e);
        }

        System.out.println("Reconstructed: " + CrudOperation.nullCheck(reconstructedObject));
    }
}

这用于测试操作类型值为CrudOperation的{​​{1}}实例的创建,这意味着Type.CREATE应为null。然而,这产生了以下输出:

op

验证了初始Initial value: {"type":"CREATE","op":null} Document value: {"@type":"CrudOperation","type":"CREATE"} isUpdate: false isNotNull: false isValid: false com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.cyberfront.test.json.nll.demonstration.CrudOperation, problem: Operation Failed Validation: {"type":"CREATE","op":null} at [Source: N/A; line: -1, column: -1] Reconstructed: null 实例化,并且该实例到CrudOperation实例的转换也可以正常工作。尝试从原始派生的JsonNode重新构建CrudOperation实例时,会出现此问题。出于某种原因,JsonNode的值显示为op,但与null相等的测试失败,这反过来导致null验证失败。

为什么会发生以及如何解决?

1 个答案:

答案 0 :(得分:1)

问题在于CrudOperation在这种情况下的含义。在有问题的情况下,显然null已经实例化,因此不是op。这可以通过在执行验证的构造函数中添加另一个输出行来显示:

null

当程序再次运行时,会产生以下输出:

System.out.println("    class:" + (null == op ? "Real Null" : op.getClass().toString()));

也就是说,Initial value: {"type":"CREATE","op":null} Document value: {"@type":"CrudOperation","type":"CREATE"} isUpdate: false isNotNull: false isValid: false class:class com.fasterxml.jackson.databind.node.NullNode com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.cyberfront.test.json.nll.demonstration.CrudOperation, problem: Operation Failed Validation: {"type":"CREATE","op":null} at [Source: N/A; line: -1, column: -1] Reconstructed: null 被实例化为op对象类型,当对其执行NullNode方法时,返回toString()作为字符串而不是类型

解决这个问题的解决方案是使用JsonNode.isNull()方法来检测何时出现这种情况。

null

@JsonCreator public CrudOperation(@JsonProperty("type") Type type, @JsonProperty("op") JsonNode op) { this.type = type; this.op = null == op || op.isNull() ? null : op; boolean isUpdate = Type.UPDATE.equals(this.getType()); boolean isNotNull = null == this.getOp(); boolean isValid = isUpdate ^ isNotNull; if (!isValid) { System.out.println(" isUpdate: " + String.valueOf(isUpdate)); System.out.println("isNotNull: " + String.valueOf(isNotNull)); System.out.println(" isValid: " + String.valueOf(isValid)); System.out.println(" class:" + (null == op ? "Real Null" : op.getClass().toString())); throw new IllegalArgumentException("Operation Failed Validation: " + this.toString()); } } 的额外检查将过滤出JsonNode是实例化isNull()节点的情况,并将提供

null

这可以达到预期的效果。