きり丸の技術日記

技術・エンジニアのイベント・資格等はこちらにまとめる予定です

【Java】JavaでXMLを比較する(特定の項目を無視するやり方)

前に、JavaJsonを比較しつつ、特定の項目を無視するやり方をする記事を書きました。

同様にJavaXMLを比較しつつ、特定の項目を無視するやり方を調べたので、記事に残しておきます。

環境

  • Java
  • 下記のどれか
    • org.assertj.core.api.Assertions.assertThat
    • org.xmlunit
    • wiremock(ただしこの記事では書きません)

構造完全一致での比較方法

まずは、構造が一致していれば、細かいところを気にしないやり方です。 その比較に関しては、assertJでもできたので、私はassertJを使用しています。

文字列での比較は、isXmlEqualToメソッドを使用します。
期待するXMLがある場合は、isXmlEqualToContentOfメソッドを使用します。

簡単な検証は文字列でもできますし、複雑な構造はファイルのまま比較できるので非常にうれしいです。

@Test
void assertXml() {
  // language=xml
  String expect =
    "<script>" +
      "     <java>12</java>    " +
    "</script>    ";

  // language=xml
  String actual =
    "<script>" +
      "<java>12</java>" +
    "</script>";
  assertThat(actual).isXmlEqualTo(expect);
  // ファイルがある場合。
  //isXmlEqualToContentOf
}

余談

assertJ(assertThat)が好きな理由は、SoftAssertionsが使える点です。
こうすることで、テストを一度に比較できるので、すごくわかりやすくなります。
アサーションルーレットの回避)

とはいえ、実際に使うときは、制約が無いassertAllの方をよく使っています。
Mockito.verify、DBUnit、xmlunitとか一度に使えますし。

過去の記事。 https://nainaistar.hatenablog.com/entry/2019/08/10/211719

特定の項目を無視する方法

xmlunitを使用すればいけます。

ただし、xmlunitのなかでも、xmlunit-placeholderが必要になります。
SpringBootではxmlunit-coreのみしか使えませんでした。
私の環境だけかもしれませんが…。

できる限りデフォルトで使いたい自分にとってはマイナスポイント。

なので、build.gradleに下記を記載して、ライブラリをimportする必要があります。

また、xmlunit-placeholderだけでなく、xmlunit-coreも一緒にimportしてあげないとバージョン違いのせいか、挙動がおかしくなりました。

testImplementation 'org.xmlunit:xmlunit-core:2.7.0'
testImplementation 'org.xmlunit:xmlunit-placeholders:2.7.0'

下記の通りにやれば、差分があるかどうかわかります。
${xmlunit.ignore} を指定している箇所は比較対象としては無視されます。

なおcompareとwithTestに渡すパラメータを間違えると、placeholderが効かなくなり、項目の無視はできません。

@Test
void assertXml2() {
  // language=xml
  String expect =
  "<script>" +
    "<java>${xmlunit.ignore}</java>" +
    "<major>${xmlunit.isNumber}</major>" +
    "<minor>${xmlunit.matchesRegex(^123.*)}</minor>" +
  "</script>";

  // language=xml
  String actual =
  "<script>" +
    "<java>12</java>" +
    "<major>12</major>" +
    "<minor>123XXXXX</minor>" +
  "</script>    ";

  Diff diff = 
    DiffBuilder.compare(expect).withTest(actual)
      .withDifferenceEvaluator(new PlaceholderDifferenceEvaluator())
      .build();
  
  assertThat(diff.getDifferences())
    .withFailMessage(diff.getDifferences().toString())
    .hasSize(0);
}

${xmlunit.ignore} 以外にもisNumberや正規表現もあります。

一覧の所在がわからなかったのですが、testクラスを読めば何が用意されているかわかりやすかったです。
なので、厳密に比較したければplacdeholderのtestクラスから探すと良いでしょう。

個人的には、ignoreさえできれば十分なので、検証はしません。


placeholderのテスト

github.com


なお、弊社ではWiremockというmockサーバでのテストを行っています。
このWiremockは内部的にxmlunitを使用しているようなので、上記のplaceholderが有効です。

mockサーバは今回の話の内容とはズレてきますので深くは話しません。
別途、どこかで調べて記事に残しておこうとは考えています。

なぜ、wiremockの話題を出したかというと、XMLの比較はmockサーバで検証すべきだと考えているからです。
JsonXMLって、対向システムとのやり取りをするために使われることが多いので。

ローカルでどんなXMLjsonを生成したとしても、最終的に送受信したファイル構造が違うのであれば比較する価値がありませんからね。

終わりに

後半の方でも書きましたが、JsonXMLを比較したいことってそうそうないと思います。

基本的にはmockサーバで吸収したほうが良いでしょうし。

ただ、ユースケースとしてはゼロではないです。

データ構造に意味を持たせられる点としては、XMLは優秀ですから。

なので、数年以内にまたXMLの比較していると思うので、その時までにこの記事が劣化していなければいいなと思いました。


もしこの記事が役に立ったのであれば、はてぶ、Twitterでの記事の拡散、twitterのフォローもよろしくお願いします。

検証ソース

AssertXml.java

github.com

過去記事

JSONの比較(assert)をする記事

nainaistar.hatenablog.com