我们有时不需要比较 JSON 文档里的所有节点,比较时需忽略一些不匹配的结点。一个现实的例子就是一个时间戳字段,它是变化的,在比较时我们必须忽略这些变化的字段。
添加 JSONassert 依赖
<!-- https://mvnrepository.com/artifact/org.skyscreamer/jsonassert --> <dependency> <groupId>org.skyscreamer</groupId> <artifactId>jsonassert</artifactId> <version>1.5.0</version> <scope>test</scope> </dependency>
请先阅读下面文章,了解 JSONassert Java library 基本概念 ,不同的比较模式和断言 JSON Objects 和 Array。
Introduction To JsonAssert Library
Compare JSON Objects Using JSONassert Library
Compare JSON Arrays Using JSONassert Library
没有直接的方法去忽略某些字段和属性,我们需要用到 CustomComparator。
JSONComparator com = new CustomComparator(JSONCompareMode.LENIENT, new Customization("salary", (o1, o2) -> true));
CustomComparator 是一个类,它间接实现 JSONComparator。 Customization 是另外一个类,CustomComparator 的构造函数接收 JSONCompareMode 和一个 Customization 类型的数组。Customization 类的构造函数接收一个包含 euqal() 方法的 ValueMatcher 接口的引用,
// Constructor of CustomComparator public CustomComparator(JSONCompareMode mode, Customization... customizations) { super(mode); this.customizations = Arrays.asList(customizations); }
为了忽略某些字段,跟 JSONassert是没有啥关系的,而是显示地让这些忽略的字段断言 PASS。如果我们从第一个 JSON 文档中获取的值为 v1, 第二个 JSON 文档中的值为 v2,v1 可以等于 或 不等于 v2. 我们就像弄了一个代理,显示地将 v1 和 v2 是相等的,或者说是匹配成功的。
上面的代码中,“salary” 是一个 JSON path,“o1” 和 “o2” 分别是从 JSON 文档中获取的值。无条件使得这两个值是相等的,返回 true。
也可以传多个 Customization 引用,因为 CustomComparator 接收的是 Customization 类型的数组。
JSONComparator com = new CustomComparator(JSONCompareMode.LENIENT, new Customization("salary", (o1, o2) -> true), new Customization("age", (o1, o2) -> true));
JSONAssert 类的重载方法 assertEquals() 接收 JSONComparator 引用参数。
JSON String 1
{ "id": 1, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 123.45 }
JSON String 2
{ "id": 1, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 125.45 }
比较时,忽略 “salary” 字段
import org.json.JSONException; import org.skyscreamer.jsonassert.Customization; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.comparator.CustomComparator; import org.skyscreamer.jsonassert.comparator.JSONComparator; public class IgnoringFieldsFromSimpleJsonObjects { public static void main(String[] args) throws JSONException { String s1 = "{\r\n" + " \"id\": 1,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 123.45\r\n" + "}"; String s2 = "{\r\n" + " \"id\": 1,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 125.45\r\n" + "}"; JSONComparator com = new CustomComparator(JSONCompareMode.LENIENT, new Customization("salary", (o1, o2) -> true)); JSONAssert.assertEquals(s1, s2, com); } }
JSON Object 1
{ "id": 1, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 123.45, "address":{ "permanent" : "KA", "city": "Bengaluru" } }
JSON Object 2
{ "id": 1, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 123.45, "address":{ "permanent" : "KA", "city": "Katihar" } }
比较时忽略 “address” 字段里面的 “city” 结点。我们需要提供忽略字段正确的 JSON path, “address.city“。
import org.json.JSONException; import org.skyscreamer.jsonassert.Customization; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.comparator.CustomComparator; import org.skyscreamer.jsonassert.comparator.JSONComparator; public class IgnoringFieldsFromNestedJsonObjects { public static void main(String[] args) throws JSONException { String s1 = "{\r\n" + " \"id\": 1,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 123.45,\r\n" + " \"address\":{\r\n" + " \"permanent\" : \"KA\",\r\n" + " \"city\": \"Bengaluru\"\r\n" + " }\r\n" + "}"; String s2 = "{\r\n" + " \"id\": 1,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 123.45,\r\n" + " \"address\":{\r\n" + " \"permanent\" : \"KA\",\r\n" + " \"city\": \"Katihar\"\r\n" + " }\r\n" + "}"; JSONComparator com = new CustomComparator(JSONCompareMode.LENIENT, new Customization("address.city", (o1, o2) -> true)); JSONAssert.assertEquals(s1, s2, com); } }
JSON Array 1
[{ "id": 1, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 123.45 }, { "id": 2, "first_name": "Animesh", "last_name": "Prashant", "married": true, "salary": 223.45 }]
JSON Array 2
[{ "id": 10, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 123.45 }, { "id": 2, "first_name": "Animesh", "last_name": "Prashant", "married": true, "salary": 223.45 }]
建设我们不想比较数组中第一个 JSON Object 的 ”id“ 字段。我们需要忽略的 JSON path 就是 “[0].id”,但是如果我们选择的是 LENIENT mode,字段是不会忽略的。针对特定索引的元素,我们应该用 STRICT mode。
import org.json.JSONException; import org.skyscreamer.jsonassert.Customization; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.comparator.CustomComparator; import org.skyscreamer.jsonassert.comparator.JSONComparator; public class IgnoringFieldsFromSimpleJsonArray { public static void main(String[] args) throws JSONException { String s1 = "[{\r\n" + " \"id\": 1,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 123.45\r\n" + "},\r\n" + "{\r\n" + " \"id\": 2,\r\n" + " \"first_name\": \"Animesh\",\r\n" + " \"last_name\": \"Prashant\",\r\n" + " \"married\": true,\r\n" + " \"salary\": 223.45\r\n" + "}]"; String s2 = "[{\r\n" + " \"id\": 10,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 123.45\r\n" + "},\r\n" + "{\r\n" + " \"id\": 2,\r\n" + " \"first_name\": \"Animesh\",\r\n" + " \"last_name\": \"Prashant\",\r\n" + " \"married\": true,\r\n" + " \"salary\": 223.45\r\n" + "}]"; JSONComparator com = new CustomComparator(JSONCompareMode.STRICT, new Customization("[0].id", (o1, o2) -> true)); JSONAssert.assertEquals(s1, s2, com); } }
如果我们想忽略所有索引元素,我们可以传递 “[*].id“ JSON path,就可以用 STRICT 或 LENIENT mode 了。
JSON Array 1
[ { "id": 1, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 123.45, "mob": [ { "type": "personal", "number": "1234566" }, { "type": "business", "number": "987654321" } ] }, { "id": 2, "first_name": "Animesh", "last_name": "Prashant", "married": true, "salary": 223.45, "mob": [ { "type": "personal", "number": "1234566" }, { "type": "business", "number": "987654321" } ] } ]
JSON Array 2
[ { "id": 1, "first_name": "Amod", "last_name": "Mahajan", "married": false, "salary": 123.45, "mob": [ { "type": "personal", "number": "1234566" }, { "type": "business", "number": "34545646" } ] }, { "id": 2, "first_name": "Animesh", "last_name": "Prashant", "married": true, "salary": 223.45, "mob": [ { "type": "personal", "number": "1234566" }, { "type": "business", "number": "987654321" } ] } ]
假设想忽略第一个索引元素中第二个 mobile 中的 ”number“ 字段,那么 json path 就是 “[0].mob[1].number“
import org.json.JSONException; import org.skyscreamer.jsonassert.Customization; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.comparator.CustomComparator; import org.skyscreamer.jsonassert.comparator.JSONComparator; public class IgnoringFieldsFromNestedJsonArray { public static void main(String[] args) throws JSONException { String s1 = "[\r\n" + " {\r\n" + " \"id\": 1,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 123.45,\r\n" + " \"mob\": [\r\n" + " {\r\n" + " \"type\": \"personal\",\r\n" + " \"number\": \"1234566\"\r\n" + " },\r\n" + " {\r\n" + " \"type\": \"business\",\r\n" + " \"number\": \"987654321\"\r\n" + " }\r\n" + " ]\r\n" + " },\r\n" + " {\r\n" + " \"id\": 2,\r\n" + " \"first_name\": \"Animesh\",\r\n" + " \"last_name\": \"Prashant\",\r\n" + " \"married\": true,\r\n" + " \"salary\": 223.45,\r\n" + " \"mob\": [\r\n" + " {\r\n" + " \"type\": \"personal\",\r\n" + " \"number\": \"1234566\"\r\n" + " },\r\n" + " {\r\n" + " \"type\": \"business\",\r\n" + " \"number\": \"987654321\"\r\n" + " }\r\n" + " ]\r\n" + " }\r\n" + "]"; String s2 = "[\r\n" + " {\r\n" + " \"id\": 1,\r\n" + " \"first_name\": \"Amod\",\r\n" + " \"last_name\": \"Mahajan\",\r\n" + " \"married\": false,\r\n" + " \"salary\": 123.45,\r\n" + " \"mob\": [\r\n" + " {\r\n" + " \"type\": \"personal\",\r\n" + " \"number\": \"1234566\"\r\n" + " },\r\n" + " {\r\n" + " \"type\": \"business\",\r\n" + " \"number\": \"34545646\"\r\n" + " }\r\n" + " ]\r\n" + " },\r\n" + " {\r\n" + " \"id\": 2,\r\n" + " \"first_name\": \"Animesh\",\r\n" + " \"last_name\": \"Prashant\",\r\n" + " \"married\": true,\r\n" + " \"salary\": 223.45,\r\n" + " \"mob\": [\r\n" + " {\r\n" + " \"type\": \"personal\",\r\n" + " \"number\": \"1234566\"\r\n" + " },\r\n" + " {\r\n" + " \"type\": \"business\",\r\n" + " \"number\": \"987654321\"\r\n" + " }\r\n" + " ]\r\n" + " }\r\n" + "]"; System.out.println(s1); System.out.println(s2); JSONComparator com = new CustomComparator(JSONCompareMode.STRICT, new Customization("[0].mob[1].number", (o1, o2) -> true)); JSONAssert.assertEquals(s1, s2, com); } }
注意,比较时如果忽略的 JSON path 在 JSON 文档中找不到, 测试将会失败。