如何比较两个JsonNode与Jackson?

6rqinv9w  于 2022-11-08  发布在  其他
关注(0)|答案(3)|浏览(309)

我有一个比较两个对象的方法,但是我不知道如何比较Jackson库的JsonNode。
我想得到这样的东西:

private boolean test(JsonNode source) {
    JsonNode test = compiler.process(file);
    return test.equals(source);
}
lf5gs5x2

lf5gs5x21#

这样就可以使用JsonNode了。等于:
节点对象的相等性被定义为完全(深度)值相等。这意味着可以通过比较根节点的相等性来比较完整的JSON树的相等性。
也可以添加一个空检查,如test != null

8i9zcol2

8i9zcol22#

您当前的代码看起来还可以,JsonNode类提供了JsonNode.equals(Object)方法用于检查:
节点对象的相等定义为完全(深度)值相等。
自2.6版起,还有使用自定义比较器的重载版本:

public boolean equals(Comparator<JsonNode> comparator, JsonNode other){
    return comparator.compare(this, other) == 0;
}
1mrurvl1

1mrurvl13#

还有一种解决方案(特别适合单元测试,因为它是可定制的)是使用一个自定义的比较器,它忽略了子节点的顺序。有各种各样的警告,您可能必须根据您的情况和序列化方法进行自定义。
下面是一个与Jackson和AssertJ一起工作的示例(但是很容易为其他框架重新编写)。

assertThat(json1)
      .usingComparator(new ComparatorWithoutOrder(true))
      .isEqualTo(json2);

首先,您应该设置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变量)

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

相关问题