Java で XML が XSD スキーマ構造を満たしているかバリデーションする
Java のクラスライブラリ javax.xml.validation
を使った XML スキーマ検証をします。スキーマエラーを全て取得する方法も紹介しています。
やりたいこと
銀行間の国際的な決済メッセージングネットワークである SWIFT のメッセージフォーマットが ISO 20022 に準拠するということで、金融機関界隈では XML 周りの処理についてよく聞かれます。今回は対象の XML が XSD で定められているスキーマ構造を満たしているか検証し、満たしていない場合はその内容を取得するということをやってみたいと思います。
XML のスキーマ検証は C ベースの XML ツールである libxml2 (GitHub - GNOME/libxml2: Read-only mirror of https://gitlab.gnome.org/GNOME/libxml2)や Java のクラスライブラリ javax.xml.validation
が 2 強で、他言語のライブラリもこれらをラップしたものであることが多いです。ここでは Java のクラスライブラリ javax.xml.validation
を使用して XML のスキーマ検証をやってみます。
環境
- Java
- Oracle Java SE 22
準備
検証の元となるスキーマ定義ファイル (xsd
) と検証される XML ファイルを用意します。
具体的に検証したい XML や XSD が手持ちである場合はそれを使ってください。
まずスキーマ定義ファイルですが、ISO 20022 Message Definitions | ISO20022から CustomerCreditTransferInitiationV12 の xsd
ファイル pain.001.001.12.xsd
をダウンロードします。
次に検証される XML ファイルとして、Sample pain.001.001.xxx Files - Goldman Sachs Developer から US Fedwire v3 をコピーし pain.001.001.03.xml
として保存します。
簡単な XML スキーマ検証
簡単な XML スキーマ検証の例
XML スキーマ検証をする最小限のコードとしては以下のようになります。準備した各ファイルは同ディレクトリに配置し、ファイル名をハードコードする形にしています。
少し長くなりますが一つ一つ見ていきます。
SchemaFactory.newInstance()
メソッドで SchemaFactory
のインスタンスを作成します。SchemaFactory.newInstance()
の引数はスキーマ言語の名前空間 URI を渡します。
Obtains a new instance of a
SchemaFactory
that supports the specified schema language. - SchemaFactory (Java SE 22 & JDK 22)
通常スキーマ言語の名前空間 URI は W3C の XML Schema 1.0 で http://www.w3.org/2001/XMLSchema
です。先ほど準備した pain.001.001.12.xsd
を見てみると xmlns:xs="http://www.w3.org/2001/XMLSchema"
と名前空間が定義されており xs:
の名前空間プレフィクス付き要素でスキーマ構造が定義されていますので、W3C の XML Schema 1.0 でスキーマが定義されていることがわかります。
W3C の XML Schema 1.0 の名前空間 URI は XMLConstants.W3C_XML_SCHEMA_NS_URI
で定義されているのでこちらを使用します。
W3C XML Schema Namespace URI. Defined to be “
http://www.w3.org/2001/XMLSchema
”.
- XMLConstants (Java SE 22 & JDK 22)
次に SchemaFactory.newSchema()
メソッドにスキーマ定義を渡して Schema
インスタンスを作成します。
Parses the specified File as a schema and returns it as a
Schema
.
- SchemaFactory (Java SE 22 & JDK 22)
さらに Schema.newValidator()
メソッドで Validator
インスタンスを作成します。
Creates a new Validator for this Schema. - Schema (Java SE 22 & JDK 22)
これで pain.001.001.12.xsd
のスキーマに沿った Validator
インスタンスが作成できました。
最後に Validator.validate()
メソッドで XML pain.001.001.03.xml
の検証を行います。
Validates the specified input. Validator (Java SE 22 & JDK 22)
Validator.validate()
メソッドは File
を直接受け取ってはくれないので StreamSource
クラスを介して XML ファイルを渡しています。
Construct a StreamSource from a File. - StreamSource (Java SE 22 & JDK 22)
Validator.validation()
メソッドはスキーマ検証でエラーが発生した場合、SAXException
を投げるのでそれを catch
して出力する形にしています。
実行およびスキーマ検証結果
ではクラスコンパイルして実行してみます。
すると以下エラーが出力されます。
pain.001.001.12.xsd
と pain.001.001.03.xml
のファイル名の最後の 12
と 03
はバージョン番号なのですが、XML 名前空間がしっかりバージョン番号まで含めて切られているため、名前空間 urn:iso:std:iso:20022:tech:xsd:pain.001.001.03
の Document
はあるけど、名前空間 urn:iso:std:iso:20022:tech:xsd:pain.001.001.12
の Document
は見つからないと言っています。
まあこれも立派な検証ではあるのですが、ちょっとつまらないので pain.001.001.03.xml
を編集して名前空間を urn:iso:std:iso:20022:tech:xsd:pain.001.001.03
から urn:iso:std:iso:20022:tech:xsd:pain.001.001.12
に変更して再度実行してみましょう。
すると以下のエラーが出ます。
pain.001.001.03.xml
の該当箇所は以下です。
一方 pain.001.001.12.xsd
では ReqdExctnDt
要素の定義は以下となっていて、DateAndDateTime2Choice
型である必要があります。
DateAndDateTime2Choice
型の定義は以下となっていて Dt
要素か DtTm
要素である必要があります。
従って、pain.001.001.03.xml
の該当箇所を以下のように修正するとエラーは解消されます。
上記修正後、再度実行するとまた別のエラーがでますが、繰り返し XML スキーマの話になるのでここでは解説しません。
全てのスキーマ検証結果を取得
ここまで見てきたように現状のコードではスキーマ検証エラーが一つずつしか出力されないので、XML を修正しては再度実行してと繰り返しになります。これはいけてないので全てのスキーマ検証結果を一度で取得できるようにしたいと思います。
エラーハンドラー
Validator
は setErrorHandler()
メソッドで ErrorHandler
を設定し、エラー発生時の挙動を設定することができます。
Sets the
ErrorHandler
to receive errors encountered during thevalidate
method invocation.
Error handler can be used to customize the error handling process during a validation. When anErrorHandler
is set, errors found during the validation will be first sent to theErrorHandler
.
- Validator (Java SE 22 & JDK 22)
なお ErrorHandler
が未設定の場合、以下の通り即時 throw
する ErrorHandler
が設定されるので、一つエラーが検知されると止まってしまっていたわけです。
When the ErrorHandler is null, the implementation will behave as if the following ErrorHandler is set:
エラーハンドラーを作成
それではカスタマイズしたエラーハンドラーを作成し、すべてのスキーマ検証エラーを取得してみましょう。カスタマイズしたエラーハンドラーは以下の ErrorHandler
インターフェースを実装する必要があります。
Basic interface for SAX error handlers.
If a SAX application needs to implement customized error handling, it must implement this interface and then register an instance with the XML reader using thesetErrorHandler
method. The parser will then report all errors and warnings through this interface. - ErrorHandler (Java SE 22 & JDK 22)
実際に実装してみた例がこちらです。スキーマ検証エラーが発生した場合、throw
するのではなく ArrayList
にエラーをためていくようにしています。
エラーハンドラーを Validator
にセットする
カスタマイズしたエラーハンドラーを使用して元のコードを書き換えてみます。Validator.setErrorHandler()
メソッドでカスタマイズしたエラーハンドラーをセットし、最後にためこんだエラーをすべて出力させています。
実行およびスキーマ検証結果
再度クラスコンパイルして実行してみます。
以下の通り全ての検証エラーを一度の実行で取得することができました。
なお、上述の以下エラーを解消していないとルート要素自体が見つからないので他の検証結果を取得することができないので注意です。
あとがき
Java のクラスライブラリ javax.xml.validation
を使用したスキーマ検証を紹介しました。ビルトインライブラリでスキーマ検証までできるなんて Java はすごいですね。XML は Office 系ファイルの Open XML や有価証券報告書等の XBRL のように様々なところで使われているので使いこなせると便利だと思います。