在Java中比较两个JSON文件的最佳方法

45

你认为应该如何处理这个任务?

我看到的挑战在于如何智能地呈现不同的信息。在我重新发明轮子之前,是否有一种通用方法来处理这样的比较?


2
在这个答案中,您将找到使用Jackson/Gson和Guava的解决方案。 - cassiomolin
Json Compare Json-Compare - Slev Florin
11个回答

33
我推荐使用zjsonpatch库,它根据RFC 6902(JSON Patch)的规范呈现差异信息。你可以与Jackson一起使用它。
import com.fasterxml.jackson.databind.{ObjectMapper, JsonNode}
JsonNode beforeNode = jacksonObjectMapper.readTree(beforeJsonString);
JsonNode afterNode = jacksonObjectMapper.readTree(afterJsonString);

import com.flipkart.zjsonpatch.JsonDiff
JsonNode patch = JsonDiff.asJson(beforeNode, afterNode);
String diffs = patch.toString();

这个库比fge-json-patch(在另一个答案中提到过)更好,因为它可以检测到数组中的插入/删除项。Fge-json-patch无法处理这一点(如果将项目插入到数组的中间,它会认为该项目和之后的每个项目都发生了变化,因为它们都向后移动了一个位置)。

1
这仅比较JSON文档的结构。 - Ean V
2
它正在比较键和值。如何使其仅针对键? - Saagar
代码对我不起作用。你可能需要首先创建一个新的 ObjectMapperObjectMapper mapper = new ObjectMapper(); JsonNode beforeNode = mapper.readTree(beforeJsonString); JsonNode afterNode = mapper.readTree(afterJsonString); JsonNode patch = JsonDiff.asJson(beforeNode, afterNode); String diffs = patch.toString(); - kohane15

15

我用过JSONAssert,体验很不错。

import org.junit.Test;
import org.apache.commons.io.FileUtils;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;

... 
@Test
public void myTest() {
  String expectedJson = FileUtils.readFileToString("/expectedFile");
  String actualJson = FileUtils.readFileToString("/actualFile");
  JSONAssert.assertEquals(expectedJson, actualJson, JSONCompareMode.STRICT);
}
... 

1
谢谢!这是我找到的唯一一个可以忽略列表中元素顺序的库。 - Saeid Nourian
这个并没有像宣传的那样工作,当我给他们发电子邮件时,邮箱地址已经无效了。废弃软件? - likejudo
1
查找JSON差异时使用JSONAssert失败 - likejudo

15

使用JSONAssert库中的JSONCompare是比较JSON字符串最简单的方式。优点是,它不仅限于结构,如果需要还可以比较值:

JSONCompareResult result = 
     JSONCompare.compareJSON(json1, json2, JSONCompareMode.STRICT);
System.out.println(result.toString());

将输出像这样的内容:

Expected: VALUE1
     got: VALUE2
 ; field1.field2

我在编译时遇到了错误:类型org.json.JSONArray无法解析。它是从所需的.class文件间接引用的。 - Vikas Suryawanshi
1
一个通用的规则是,如果你想使用一个库,就要包含它的所有依赖项,或者使用Maven自动获取依赖项。 - Ean V
这个并没有像宣传的那样工作,当我给他们发电子邮件时,发现该电子邮件地址已经无效了。废弃软件? - likejudo
查找JSON差异时使用JSONAssert失败 - likejudo

13
这仅涉及相等性,不涉及差异。
使用Jackson。
ObjectMapper mapper = new ObjectMapper();

JsonNode tree1 = mapper.readTree(jsonInput1);
JsonNode tree2 = mapper.readTree(jsonInput2);

boolean areTheyEqual = tree1.equals(tree2);

JsonNode.equals 的JavaDoc文档 中得知:

节点对象的相等性被定义为完全(深层)值的相等。这意味着可以通过比较根节点的相等性来比较完整的JSON树是否相等。


13
如果 JSON 字段顺序不同,mapper.readTree 将无法正常工作。 - Cherry
2
仅比较JSON文档的结构。 - Ean V
它正在比较键和值。如何使其仅针对键? - Saagar
1
@EanV 和 Cherry 都不正确。它比较的是值,即使成员顺序不同也一样。 - likejudo
2
@Cherry json字段可以有不同的顺序。但是,只有当两个列表元素具有完全相同的值和顺序时,它们才被视为相等。 - dr0i

5

4
我曾经遇到过类似的问题,最终我写了自己的库:https://github.com/algesten/jsondiff。它可以实现差异比较和补丁操作。差异比较结果本身就是JSON对象,并且具有简单的语法,用于合并/替换对象和插入/替换数组。例如:
original
{
   a: { b: 42 }
}

patch
{
  "~a": { c: 43 }
}

~表示对象合并。

result
{
   a: { b: 42, c: 43 }
}

仅限于文档结构的限制。 - Ean V
2
不是。它也比较值,但我不建议使用它,因为我遇到了一些难以解决的边缘情况,并失去了兴趣。 - Martin Algesten

2
您可以使用下面的依赖项以STRICT、LENIENT、IGNORE_ORDER等方式比较Json。

<dependency>
    <groupId>org.skyscreamer</groupId>
    <artifactId>jsonassert</artifactId>
    <version>1.5.0</version>
    <scope>test</scope>
</dependency>

  public static boolean isTwoJsonEquals(JSONObject firstObj,JSONObject secondObj, JSONCompareMode compareMode){
    try {
      JSONCompareResult result = JSONCompare.compareJSON(firstObj, secondObj, compareMode);

      return result.passed();
    } catch (JSONException e) {
      e.printStackTrace();
    }
    return false;
  }

1

对于已经使用Jackson的人,我建议使用json-patch

final JsonNode patchNode = JsonDiff.asJson(firstNode, secondNode);
System.out.println("Diff=" + m.writeValueAsString(patchNode));

1
我访问了他们的网站并发现它是在LGPLv3下授权的。如果我错了,请纠正我,但是LGPL对于公司使用来说不是非常严格吗? - likejudo

1

json-lib

我会使用json-lib解析JSON数据。这将生成普通的Java对象,你可以使用equals方法进行比较。不过,这只适用于假设 json-lib 的开发人员正确实现了equals方法,不过你可以轻松进行测试。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接