自分用記事。
JSON比較に手間取ったので、未来の自分に役に立つはず。
なお可能であれば、assertThatのメソッドチェインに加えたいので、もしそっちで比較できることを知っていれば教えてほしいです…。
環境
- org.skyscreamer.jsonassert
- JSONAssertで読み込めなければ、絶対パス(?)で記載してください。
org.skyscreamer.jsonassert.JSONAssert
のように。
- JSONAssertで読み込めなければ、絶対パス(?)で記載してください。
- Java
- Junit5
ソースコード
やりたいこと
JSONの構造での比較。
半角スペースや意図しない改行で失敗しないようにしたい。
構造上合っているのであれば、その順番は気にしたくない。
下記は一致しているとみなしたい。
{ "a": "111", "b": "999" }
{ "b": "999", "a": "111" }
通常比較
Escape文字が入って見づらいですが、こういうjsonを比較しようとしています。
expectのJson { "animal": "gorilla", "age": 18 }
actualのJson { "age": 18, "animal": "gorilla" }
@Test void test_00() throws JSONException { // language=json String expect = "{\"animal\": \"gorilla\",\n" + " \"age\": 18\n" + "}\n"; // language=json String actual = "{ \"age\": 18," + "\"animal\": \"gorilla\"\n" + "}\n"; // この第三引数は、配列項目を厳密に比較するか、比較しないかを設定します。 // 厳密に比較したくなければ、JSONCompareMode.LENIENTを使用してください。 JSONAssert.assertEquals(expect, actual, JSONCompareMode.STRICT); }
ちゃんと、配列の順番が変わっていた場合に、NGであることを比較するソースも作ったので、良ければローカルで確認してください。
EnumSourceってあまり使わないと思ってましたが、こういう時の検証に便利ですね。
expectのjson { "animal": [ { "id": 1, "name": "gorilla", "age": 18 }, { "id": 2, "name": "gorilla", "age": 22 } ] }
actualのjson { "animal": [ { "age": 22, "name": "gorilla", "id": 2 }, { "id": 1, "name": "gorilla", "age": 18 } ] }
@EnumSource(value = JSONCompareMode.class) @ParameterizedTest void test_01(JSONCompareMode mode) throws JSONException { // language=json String expect = "{ \n" + " " + "\"animal\": [\n" + " {\n" + " " + "\"id\": 1,\n" + " \"name\": \"gorilla\",\n" + " \"age\": 18\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"name\": \"gorilla\",\n" + " \"age\": 22\n" + " }\n" + " ]\n" + "}\n"; // language=json String actual = "{ \n" + " " + "\"animal\": [\n" + " {\n" + " \"age\": 22,\n" + " \"name\": \"gorilla\",\n" + " \"id\": 2\n" + " },\n" + " {\n" + " \"id\": 1,\n" + " \"name\": \"gorilla\",\n" + " \"age\": 18\n" + " }\n" + " ]\n" + "}\n"; JSONAssert.assertEquals(expect, actual, mode); }
追加でやりたいこと
全ての項目を完全一致させたくないこともあると思います。
例えば、特定の項目はランダムな認証キーを発行してたり、応答時間を返してたりしていると、完全一致は難しくなります。
なので、一部比較項目として除外する方法を探しました。
特定の項目を完全無視するやり方
以下のソースであれば、time.insertTime, time.updateTimeを無視できます。
@Test void test_01() throws JSONException { // language=json String expect = "{ \"time\" : {\n" + " \"insertTime\": 201912010023,\n" + " \"updateTime\": 202008111234\n" + "}," + "\"animal\": \"gorilla\"}\n"; // language=json String actual = "{ \"time\" : {\n" + " \"insertTime\": 201912010023,\n" + " \"updateTime\": 999999999999\n" + "}," + "\"animal\": \"gorilla\"}\n"; JSONAssert.assertEquals(expect, actual, new CustomComparator(JSONCompareMode.STRICT, new Customization("time.insertTime", (o1, o2) -> true), new Customization("time.updateTime", (o1, o2) -> true) ) ); }
特定の項目を正規表現に沿っていなかったら無視
RegularExpressionValueMatcher を使用することで、正規表現に合っていなかった場合にエラーにさせることができます。
ただ、yyyyMMdd等で比較することはできなかったので、あんまり使えないんじゃないかな…。
なお、下記例ではinsertTimeが2019…なので、2020の正規表現に一致せず失敗します。
@Test void test_02() throws JSONException { // language=json String expect = "{ \"time\" : {\n" + " \"insertTime\": 201912010023,\n" + " \"updateTime\": 202008111234\n" + "}," + "\"animal\": \"gorilla\"}\n"; // language=json String actual = "{ \"time\" : {\n" + " \"insertTime\": 201912010023,\n" + " \"updateTime\": 202012300123\n" + "}," + "\"animal\": \"gorilla\"}\n"; JSONAssert.assertEquals(expect, actual, new CustomComparator(JSONCompareMode.STRICT, new Customization("time.insertTime", new RegularExpressionValueMatcher<>(".*2020.*")), new Customization("time.updateTime", new RegularExpressionValueMatcher<>(".*2020.*")) ) ); }