考虑以下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
验证失败。
为什么会发生以及如何解决?
答案 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
这可以达到预期的效果。