斷言 JSON 物件集合忽略順序
一、簡介
斷言 JSON 物件的集合相等可能具有挑戰性,尤其是當集合中元素的順序無法保證時。雖然可以使用 Jackson 和 AssertJ 等庫,但 JSONassert 和hamcrest-json
等更專業的工具旨在更可靠地處理此用例。
在本教程中,我們將探索如何比較 JSON 物件的集合,重點是使用 JSONassert 和hamcrest-json
忽略元素的順序。
2. 問題陳述
當我們使用 JSON 物件集合時,列表中元素的順序可能會根據資料來源而變化。
考慮以下 JSON 數組:
[
{"id": 1, "name": "Alice", "address": {"city": "NY", "street": "5th Ave"}},
{"id": 2, "name": "Bob", "address": {"city": "LA", "street": "Sunset Blvd"}}
]
[
{"id": 2, "name": "Bob", "address": {"city": "LA", "street": "Sunset Blvd"}},
{"id": 1, "name": "Alice", "address": {"city": "NY", "street": "5th Ave"}}
]
儘管這些數組具有相同的元素,但順序不同。儘管這些數組的數據相同,但由於順序差異,直接對這些數組進行字串比較將失敗。
讓我們將這些 JSON 數組定義為 Java 變量,然後探索如何在忽略順序的情況下比較它們的相等性:
String jsonArray1 = "["
+ "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}}, "
+ "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}}"
+ "]";
String jsonArray2 = "["
+ "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}}, "
+ "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}}"
+ "]";
3.使用JSONassert進行JSON比較
JSONassert 提供了一種靈活的方法來比較 JSON 數據,讓我們可以將 JSON 物件或陣列作為 JSON 進行比較,而不是直接進行字串比較。具體來說,它可以比較數組,同時忽略元素的順序。
在LENIENT
模式下, JSONAssert
純粹專注於內容,忽略順序:
@Test
public void givenJsonArrays_whenUsingJSONAssertIgnoringOrder_thenEqual() throws JSONException {
JSONAssert.assertEquals(jsonArray1, jsonArray2, JSONCompareMode.LENIENT);
}
在此測試中, JSONCompareMode.LENIENT
模式允許我們在忽略元素順序的同時斷言相等。這使得 JSONassert 非常適合我們期望相同資料但元素順序可能不同的情況。
3.1.使用JSONassert
忽略額外字段
JSONassert 還允許使用相同的LENIENT
模式忽略 JSON 物件中的額外欄位。這在比較 JSON 資料時非常有用,其中某些欄位(例如元資料或時間戳記)與測試無關:
@Test
public void givenJsonWithExtraFields_whenIgnoringExtraFields_thenEqual() throws JSONException {
String jsonWithExtraFields = "["
+ "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}, \"age\": 30}, "
+ "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}, \"age\": 25}"
+ "]";
JSONAssert.assertEquals(jsonArray1, jsonWithExtraFields, JSONCompareMode.LENIENT);
}
在此範例中,測試驗證jsonArray1
等效於jsonWithExtraFields
,允許在比較中使用額外的欄位(例如age
。
4.使用hamcrest-json
進行JSON匹配
除了 JSONassert 之外,我們還可以利用hamcrest-json ,這是專為 JSON 斷言設計的 Hamcrest 外掛。該插件基於 Hamcrest 的匹配器功能構建,允許我們在 JUnit 中使用 JSON 時編寫富有表現力且可讀的斷言。
hamcrest-json
中最有用的功能之一是allowingAnyArrayOrdering()
方法。這使我們能夠比較 JSON 數組,同時忽略它們的順序:
@Test
public void givenJsonCollection_whenIgnoringOrder_thenEqual() {
assertThat(jsonArray1, sameJSONAs(jsonArray2).allowingAnyArrayOrdering());
}
此方法可確保 JSON 相等性檢查使用sameJSONAs()
匹配器忽略數組中元素的順序。
4.1.使用hamcrest-json
忽略額外字段
除了忽略數組排序之外, hamcrest-json
還提供allowingExtraUnexpectedFields()
實用程式來處理額外欄位。此方法使我們能夠忽略一個 JSON 物件中存在的字段,但不能忽略另一個 JSON 物件中存在的字段:
@Test
public void givenJsonWithUnexpectedFields_whenIgnoringUnexpectedFields_thenEqual() {
String jsonWithUnexpectedFields = "["
+ "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}, \"extraField\": \"ignoreMe\"}, "
+ "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}}"
+ "]";
assertThat(jsonWithUnexpectedFields, sameJSONAs(jsonArray1).allowingExtraUnexpectedFields());
}
在此範例中,我們驗證jsonWithUnexpectedFields
是否等於jsonArray1
,即使它包含額外的欄位。透過將allowingExtraUnexpectedFields()
與allowingAnyArrayOrdering()
結合,我們確保了穩健的比較,重點是匹配JSON 數組之間的資料。
5. 結論
在本文中,我們示範了透過使用 JSONassert 和 hamcrest-json 等專用函式庫來比較 JSON 物件集合,同時忽略元素順序的能力。這些函式庫提供了一種比手動將 JSON 解析為 Java 物件更直接的方法,從而提供了更高的可靠性和易用性。
與往常一樣,本文的完整程式碼範例可以在 GitHub 上找到。