我有一个比较两个对象的方法,但是我不知道如何比较Jackson库的JsonNode。我想得到这样的东西:
private boolean test(JsonNode source) { JsonNode test = compiler.process(file); return test.equals(source); }
lf5gs5x21#
这样就可以使用JsonNode了。等于:节点对象的相等性被定义为完全(深度)值相等。这意味着可以通过比较根节点的相等性来比较完整的JSON树的相等性。也可以添加一个空检查,如test != null
test != null
8i9zcol22#
您当前的代码看起来还可以,JsonNode类提供了JsonNode.equals(Object)方法用于检查:节点对象的相等定义为完全(深度)值相等。自2.6版起,还有使用自定义比较器的重载版本:
JsonNode
JsonNode.equals(Object)
public boolean equals(Comparator<JsonNode> comparator, JsonNode other){ return comparator.compare(this, other) == 0; }
1mrurvl13#
还有一种解决方案(特别适合单元测试,因为它是可定制的)是使用一个自定义的比较器,它忽略了子节点的顺序。有各种各样的警告,您可能必须根据您的情况和序列化方法进行自定义。下面是一个与Jackson和AssertJ一起工作的示例(但是很容易为其他框架重新编写)。
assertThat(json1) .usingComparator(new ComparatorWithoutOrder(true)) .isEqualTo(json2);
首先,您应该设置ObjectMapper,以便以某种可预测的方式序列化日期,例如:
ObjectMapper
mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
否则,根据创建JsonNode的方式,您最终会将一些Instant字段序列化为TextNode,将一些字段序列化为DecimalNode。另外,对于value: null这样的字段,可能会有问题,它们可能出现在一个JSON中,而在另一个JSON中却丢失了。另一个警告是,您正在序列化Set类型的字段,您将得到JSON数组(具有一定的顺序),并且需要额外的工作来处理这种情况。这是一个元信息的示例,在从Java Set序列化到JSON数组的过程中丢失了元信息。下面是一个比较器示例,它可以配置为忽略JSON数组中的元素顺序,或者关注元素顺序(参见ignoreElementOrderInArrays变量)
Instant
TextNode
DecimalNode
value: null
Set
ignoreElementOrderInArrays
import static java.util.Spliterators.spliteratorUnknownSize; import static java.util.stream.StreamSupport.stream; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import com.google.common.collect.Sets; import java.util.Collection; import java.util.Comparator; import java.util.Optional; import java.util.Spliterator; class ComparatorWithoutOrder implements Comparator<Iterable<? extends JsonNode>> { private boolean ignoreElementOrderInArrays; public ComparatorWithoutOrder(boolean ignoreElementOrderInArrays) { this.ignoreElementOrderInArrays = ignoreElementOrderInArrays; } @Override public int compare(Iterable<? extends JsonNode> o1, Iterable<? extends JsonNode> o2) { if (o1 == null || o2 == null) { return -1; } if (o1 == o2) { return 0; } if (o1 instanceof JsonNode && o2 instanceof JsonNode) { return compareJsonNodes((JsonNode) o1, (JsonNode) o2); } return -1; } private int compareJsonNodes(JsonNode o1, JsonNode o2) { if (o1 == null || o2 == null) { return -1; } if (o1 == o2) { return 0; } if (!o1.getNodeType().equals(o2.getNodeType())) { return -1; } switch (o1.getNodeType()) { case NULL: return o2.isNull() ? 0 : -1; case BOOLEAN: return o1.asBoolean() == o2.asBoolean() ? 0 : -1; case STRING: return o1.asText().equals(o2.asText()) ? 0 : -1; case NUMBER: double double1 = o1.asDouble(); double double2 = o2.asDouble(); return Math.abs(double1 - double2) / Math.max(double1, double2) < 0.999 ? 0 : -1; case OBJECT: // ignores fields with null value that are missing at other JSON var missingNotNullFields = Sets .symmetricDifference(Sets.newHashSet(o1.fieldNames()), Sets.newHashSet(o2.fieldNames())) .stream() .filter(missingField -> isNotNull(o1, missingField) || isNotNull(o2, missingField)) .toList(); if (!missingNotNullFields.isEmpty()) { return -1; } Integer reduce1 = stream(spliteratorUnknownSize(o1.fieldNames(), Spliterator.ORDERED), false) .map(key -> compareJsonNodes(o1.get(key), o2.get(key))) .reduce(0, (a, b) -> a == -1 || b == -1 ? -1 : 0); return reduce1; case ARRAY: if (o1.size() != o2.size()) { return -1; } if (o1.isEmpty()) { return 0; } var o1Iterator = o1.elements(); var o2Iterator = o2.elements(); var o2Elements = Sets.newHashSet(o2.elements()); Integer reduce = stream(spliteratorUnknownSize(o1Iterator, Spliterator.ORDERED), false) .map(o1Next -> ignoreElementOrderInArrays ? lookForMatchingElement(o1Next, o2Elements) : compareJsonNodes(o1Next, o2Iterator.next())) .reduce(0, (a, b) -> a == -1 || b == -1 ? -1 : 0); return reduce; case MISSING: case BINARY: case POJO: default: return -1; } } private int lookForMatchingElement(JsonNode elementToLookFor, Collection<JsonNode> collectionOfElements) { // Note: O(n^2) complexity return collectionOfElements.stream() .filter(o2Element -> compareJsonNodes(elementToLookFor, o2Element) == 0) .findFirst() .map(o2Element -> 0) .orElse(-1); } private static boolean isNotNull(JsonNode jsonObject, String fieldName) { return Optional.ofNullable(jsonObject.get(fieldName)) .map(JsonNode::getNodeType) .filter(nodeType -> nodeType != JsonNodeType.NULL) .isPresent(); } }
3条答案
按热度按时间lf5gs5x21#
这样就可以使用JsonNode了。等于:
节点对象的相等性被定义为完全(深度)值相等。这意味着可以通过比较根节点的相等性来比较完整的JSON树的相等性。
也可以添加一个空检查,如
test != null
8i9zcol22#
您当前的代码看起来还可以,
JsonNode
类提供了JsonNode.equals(Object)
方法用于检查:节点对象的相等定义为完全(深度)值相等。
自2.6版起,还有使用自定义比较器的重载版本:
1mrurvl13#
还有一种解决方案(特别适合单元测试,因为它是可定制的)是使用一个自定义的比较器,它忽略了子节点的顺序。有各种各样的警告,您可能必须根据您的情况和序列化方法进行自定义。
下面是一个与Jackson和AssertJ一起工作的示例(但是很容易为其他框架重新编写)。
首先,您应该设置
ObjectMapper
,以便以某种可预测的方式序列化日期,例如:否则,根据创建JsonNode的方式,您最终会将一些
Instant
字段序列化为TextNode
,将一些字段序列化为DecimalNode
。另外,对于
value: null
这样的字段,可能会有问题,它们可能出现在一个JSON中,而在另一个JSON中却丢失了。另一个警告是,您正在序列化
Set
类型的字段,您将得到JSON数组(具有一定的顺序),并且需要额外的工作来处理这种情况。这是一个元信息的示例,在从JavaSet
序列化到JSON数组的过程中丢失了元信息。下面是一个比较器示例,它可以配置为忽略JSON数组中的元素顺序,或者关注元素顺序(参见
ignoreElementOrderInArrays
变量)