拡張XMLのバリデーション

XSLTやJava拡張機能を使ってXMLドキュメントの複雑な制約をバリデーションする

XSLTスタイルシートは、XMLドキュメントの変換を目的として設計されています。また、文法ベースのバリデーションでは必要な制約をすべてカバーできないときには、Java拡張機能とスタイルシートを組み合わせることで、XMLスキーマを機能強化することもできます。ここでは、XSLTとJava拡張機能を使ってドキュメントをバリデーションする場合を取り上げ、実用的なガイダンスとサンプルコードを示します。

Peter Heneback, Consultant, IBM

Photo of Peter HenebackPeter Henebackは最近コンピューターサイエンスの理学修士号を取得しました。現在はIBM United Kingdom Limitedのコンサルタントとして、統合テクノロジー、Java、XMLを専門としています。また、グリッド実装とWebサービス実装の両方の業務経験があり、グリッドセキュリティに関する記事も以前に公開しています。



2006年 5月 09日

背景

XMLスキーマやDTDなどの文法ベースのバリデーション言語には、詳しく定義されたメッセージ構造にXMLドキュメントが従っているかどうかを確認するための諸機能が備わっています。これにより受信アプリケーションが着信したXMLメッセージを正しく処理できるようになりますが、そのメッセージに含まれているデータの有効性が保証されるわけではありません。文法ベースのバリデーション言語に制限があるということは、たとえば同時発生制約のバリデーションや、変数や外部データセットと照合した制約のバリデーションに、別の手段を用いる必要があることを意味します。

多くの場合、XMLスキーマやDTDで実装できないバリデーションロジックはアプリケーションコードに組み込まれます。この解決法は実装が比較的簡単ですが、柔軟性が低くなることがあります。ここではまず、前述の問題の解決策の1つであるSchematronについて検証した後、このアプローチの短所をいくつか取り上げます。また、定評のあるW3C標準コンポーネントとJava拡張機能やオープンソースのXSLTプロセッサーを組み合わせた別の解決策についても説明します。


Schematron

XMLスキーマを補完する目的でよく提案される解決策の1つに、Schematronの使用があります。Schematronは、XPathを使ってXMLインスタンスドキュメント内のコンテンツに関するアサーションを表現するルールベースの言語です。これを行うには、基本スタイルシートを使ってSchematronスキーマを変換し、このスキーマをXSLTスタイルシートにします。このスタイルシートは、XSLTプロセッサーを通してXMLインスタンスドキュメントを実行することで、定義されたアサーションをチェックします。この変換の結果、XML形式のレポートが作成されます。このレポートには、失敗したアサーションに関する詳細情報と、スキーマ内の特定のルールが指定されたコメントが含まれています。ただしSchematronは構造の定義にはあまり適していません(すぐに手間のかかる作業になってしまうため)。したがって、XMLスキーマを使ってまずドキュメントをバリデーションする必要がありますが、XMLスキーマとSchematronを組み合わせることで大半のアプリケーションのバリデーション要件をカバーすることができます。実はSchematronのアサーションはXMLスキーマの文法とは異なるネームスペースに属しているため、通常はこの2つを同じファイルに組み込んだ後、ドキュメントバリデーションプロセスの一部としてそれぞれを分けることができます。

図1は、ごく一般的なアプリケーションシナリオの論理的な処理ステップを示しています。XSLTはまず、着信したXMLインスタンスドキュメントをバリデーションしてから、それを変換します。その後、インスタンスドキュメントはアプリケーション自身によって処理されるか、または外部アプリケーションへ送信されます。この図からもわかるように、XMLスキーマとSchematronの組み合わせを使って最初にバリデーションを実行してから、XSLTスタイルシートを使ってバリデーション済みドキュメントを変換する場合、これは複雑な処理になります。これらのスキーマを分け、Schematronスキーマを事前に変換しておけばこのプロセスを短縮できますが、それでもXSLTプロセッサーを通して2回実行する必要があり、さらに抽出されたSchematron変換によって生成されたバリデーションレポートを分析、調査する必要もあります。

図1. Schematronを使ったバリデーション
Schematronを使ったバリデーション

Schematronの短所

  • 基本スタイルシートを使ってSchematronスキーマを少なくとも1回変換しないと、それをXMLドキュメントのバリデーションに使うことはできません。Schematronの構文がXMLスキーマに組み込まれている場合は、さらに変換が必要です。
  • Schematronでは現在のところ、アプリケーションに直接レポートを返すことはできません。バリデーションの後に手動または自動でレポート処理ステップを実行する必要があります。

XSLTとJava拡張機能

このセクションでは、ルールベースのアプローチを使ったXMLスキーマによる文法ベースのバリデーションの代替手段として、XSLTとJava拡張機能を使う方法について説明します。ここでは、単純な例を示しながらこの実行方法について説明していきます。なお、XMLスキーマを使ってXMLバリデーターを実装する方法については説明しません。これについては、developerWorksで提供されている別の記事やチュートリアルで詳しく扱われています(「参考文献」を参照)。

図2図1と同じシナリオを示していますが、XSLTとJava拡張機能を使ってバリデーションと変換を1つのステップで実行しています。別途処理が必要なレポートを生成するのではなく、Exceptionクラスを拡張したValidationExceptionクラスのオブジェクトとして、バリデーションエラーが制御側アプリケーションに直接スローされます。ドキュメントが通過する必要のあるXSLTプロセッサーの数が減るうえに、最初のバリデーションエラーが発生した時点で変換が一s時停止されるため、無効なデータが無駄に処理されるのを防ぐことができます。

図2. XSLTとJava拡張機能
XSLTとJava拡張機能

XMLドキュメントの単純変換

ここでは、XSLTとJavaを使ったバリデーション方法を説明するために、サンプル入力として名前、電話番号、タイトル、性別などの従業員情報が含まれたレジストリーの単純な変換を行います。後述のとおり、タイトルと性別の情報は同時発生制約の例の中央に示されます。リスト1には、入力XMLドキュメントの一部が含まれています。

リスト1. 入力XMLドキュメント
<input:staff xmlns:input="cross-field-validation-namespace">

...

  <input:employee id="1234A">
    <input:first_name>Julia</input:first_name>
    <input:last_name>Smith</input:last_name>
    <input:title>Mrs</input:title>
    <input:gender>F</input:gender>
    <input:telephones>
      <input:mobile preferred="false">0770-555 1231</input:mobile>
      <input:mobile preferred="true">0771-555 1232</input:mobile>
      <input:home preferred="false">0207-555 1233</input:home>
    </input:telephones>
  </input:employee>

...

</input:staff>

この例に示す単純な変換では、employeeレジストリーから名字と名前が連結されます。また、リスト2に示すように、preferred属性がtrueに設定された電話番号情報も抽出されます。

リスト2. 従業員データの単純変換
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:input="cross-field-validation-namespace">

  <xsl:template match="/input:staff">
    <phones>
        <xsl:apply-templates select="input:employee" />
    </phones>
  </xsl:template>

  <xsl:template match="input:employee">
    <employee>
      <name>
        <xsl:value-of select="concat(input:first_name,' ',input:last_name)" />
      </name>
      <tel>
        <xsl:value-of select="input:telephones/*[@preferred = 'true']" />
      </tel>
    </employee>
  </xsl:template>

</xsl:stylesheet>

リスト2のXSLTを使って入力ドキュメントを変換すると、リスト3に示す出力ドキュメントが作成されます。

リスト3. 出力ドキュメント
<phones xmlns:input="cross-field-validation-namespace"
  xmlns:exception="xfield.exception.ValidationExceptionThrower"
  xmlns:xalan="http://xml.apache.org/xslt">
  <employee>
    <name>Julia Smith</name>
    <tel>0771-555 1232</tel>
  </employee>
  <employee>
    <name>John Smith</name>
    <tel>0207-555 1236</tel>
  </employee>
  <employee>
    <name>Jenny Smith</name>
    <tel>0770-555 1237</tel>
  </employee>
</phones>

単純な同時発生制約のバリデーションの実装

XSLTプロセッサーを通して制御側アプリケーションにバリデーションエラーを直接返すために必要なものは、ごく単純な2つのJavaクラスのみです。この場合は、ValidationExceptionクラスとValidationExceptionThrowerクラスを作成する必要があります。ValidationExceptionクラスは標準のJava Exceptionクラスの単純拡張です。このクラスは、制御側アプリケーションがバリデーションエラーを、プロセッサーからスローされた他の例外と区別できるようにするものです。ValidationExceptionThrowerクラスは、throwExceptionメソッドが呼び出されたときに単にValidationExceptionをスローします。また、この場合には別のクラスも必要となります。XSLTでJava拡張機能を使う場合は、Javaの通常のthrow構文を使って例外をスローすることができません。XSLTにJava拡張機能を使用できるのは、オブジェクトや呼び出しメソッドを作成する場合のみです。リスト4とリスト5は、Java拡張機能に必要な2つのクラスの完全なソースコードを示しています。

リスト4. ValidationExceptionクラス
package xfield.exception;

public class ValidationException extends Exception{

  public ValidationException(String sMsg)
  {
    super(sMsg);
  } // end constructor

} // end class
リスト5. ValidationExceptionThrowerクラス
package xfield.exception;

public class ValidationExceptionThrower {

  public ValidationExceptionThrower()
  {
    // Emtpy
  } // end constructor

  public void throwException(String sMessage) throws Exception
  {
    throw new ValidationException(sMessage);
  } // end throwException

} // end class

同時発生制約のチェック

前述のとおり、タイトルと性別は一般的な同時発生制約の例として使われています。たとえば、titleがMrに設定されている場合は、genderもM (男性)に設定されているべきなので、タイトル要素と性別要素が合っているかどうかをチェックする必要があります。タイトル要素と性別要素のそれぞれに有効な値が含まれているかどうかをバリデーションするには、XMLスキーマの列挙を使うのがベストですが、これについてはここではテストしません。

バリデーションを行うには、<choose/>文節の<otherwise/>ブロック内に変換コードを置き、<when/>ブロック内のtestステートメントとして条件をチェックします。このテストでfalseと評価された場合、つまり条件のバリデーションが正しく行われた場合は、変換コードが実行されます。このテストでtrueと評価された場合は、Java拡張機能を使ってValidationExceptionThrowerクラスのインスタンスでthrowException()メソッドが呼び出されます。XSLTのrootタグにクラス名(パッケージを含む)が含まれた、exceptionプレフィックス付きのネームスペースを追加する際には注意してください。このプレフィックスはオブジェクト名と同様に、エラー文字列を引数としてthrowException()メソッドを呼び出すのに使われます。ValidationExceptionThrowerクラスのJavaコードに戻って見ると、XSLTからのエラー文字列が含まれるValidationExceptionをスローする方法が簡単にわかります。

さらに条件をチェックするには、必要な<when/>ステートメントと、メソッドへの引数として指定された適切なエラーメッセージを追加します。

リスト6. 基本的な変換と同時発生制約のバリデーション
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:exception="xfield.exception.ValidationExceptionThrower"
  xmlns:input="cross-field-validation-namespace">

...

<xsl:template match="input:employee">
  <xsl:choose>
    <xsl:when test="input:gender = 'M' and input:title != 'Mr' 
                 or input:gender = 'F' and input:title = 'Mr'">
      <xsl:value-of
        select="exception:throwException('Gender and title do not match')"/>
    </xsl:when>
    <xsl:otherwise>
       <employee>
         <name>
           <xsl:value-of select="concat(input:first_name,' ',input:last_name)"/>
         </name>
         <tel>
           <xsl:value-of select="input:telephones/*[@preferred = 'true']"/>
         </tel>
       </employee>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

XPath拡張機能を例外メッセージに追加すると、リスト7に示すようにバリデーションエラーの詳細説明が示されるため、問題を特定しやすくなります。

リスト7. バリデーションエラーの詳細情報
<xsl:when test="input:gender = 'M' and input:title != 'Mr' 
          or input:gender = 'F' and input:title = 'Mr'">
  <xsl:value-of select="exception:throwException(
                  concat('Gender and title do not match for employee ',
                          input:first_name,' ',input:last_name))" />
</xsl:when>

バリデーションと変換の分離

変換ロジックとバリデーションロジックを別々に保つ必要がある場合は、標準の<include/>命令を使ってバリデーションチェックを参照し、変換コードを使ってそれらをまとめて実行します。リスト8は、ファイルvalidate.xslを参照する<include/>タグを使った変換コードを示しています。また、validateという名前のテンプレートの呼び出しが追加されている点にも注意してください。この呼び出しは、組み込みファイル内の条件チェックが含まれたテンプレートの名前と一致していることが重要です(リスト9を参照)。

リスト8. 抽出された変換ロジック
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:input="cross-field-validation-namespace">

  <xsl:include href="validate.xsl" />

  <xsl:template match="/">
    <xsl:call-template name="validate" />
    <phones>
      <xsl:apply-templates />
    </phones>
  </xsl:template>

  <xsl:template match="input:staff/input:employee">
    <employee>
      <name>
        <xsl:value-of
          select="concat(input:first_name,' ',input:last_name)" />
      </name>
      <tel>
        <xsl:value-of
          select="input:telephones/*[@preferred = 'true']" />
      </tel>
    </employee>
  </xsl:template>

</xsl:stylesheet>
リスト9. 抽出されたバリデーションロジック
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:exception="xfield.exception.ValidationExceptionThrower"
  xmlns:input="cross-field-validation-namespace">

  <xsl:template name="validate">
    <xsl:for-each select="/input:staff/input:employee">
      <xsl:if
        test="input:gender = 'M' and input:title != 'Mr' 
               or input:gender = 'F' and input:title = 'Mr'">
        <xsl:value-of
          select="exception:throwException('Gender and title do not match')" />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

外部参照データとの照合バリデーション

XMLスキーマでは列挙を使って、着信データと事前定義された許容値セットを比較することができます。ただし参照データが変数で、それ自体に関係が含まれている場合は、XMLスキーマでそのデータと照合バリデーションすることはできません。次の例に示すように、XSLTとJava拡張機能を使った解決策ならばこのシナリオに対応でき、変数や外部XMLデータセットと照合バリデーションすることができます。なお、変換されたスキーマはXSLTを使ってバリデーションを実行するため、Schematronでもこの機能を実現できます。

リスト10. 参照データ
<roles>
...
  <employee id="1234A" role="A"/>
  <employee id="1234D" role="A"/>
  <employee id="1234C" role="X"/>
  <employee id="1234B" role="Z"/>
  <employee id="1234X" role="Z"/>
...    
</roles>

この例の外部参照データは、役職が割り当てられた従業員データのセットです。着信したXMLドキュメント内のすべての従業員IDがこの参照データセット内に存在するかどうかをチェックするには、document()関数を使って外部XMLドキュメントを変数にロードします。従業員番号が有効であることをアサートするには、現在のIDを持つ1人以上の従業員が参照変数内に存在するかどうかをチェックします。

リスト11. 外部参照データとの照合バリデーション
<xsl:variable name="reference" select="document('reference.xml')" />

<xsl:template name="validate">
  <xsl:for-each select="/input:staff/input:employee">

    <xsl:if test="input:gender = 'M' and input:title != 'Mr' 
           or input:gender = 'F' and input:title = 'Mr'">
      <xsl:value-of
        select="exception:throwException(concat('Gender and title do not match 
               for employee ',input:first_name,' ',input:last_name))" />
    </xsl:if>

    <xsl:variable name="current_id" select="@id" />

    <xsl:if
      test="count($reference/roles/employee[@id = $current_id]) = 0">
      <xsl:value-of
        select="exception:throwException(concat('Invalid employee ID: ',$current_id))" />
    </xsl:if>

  </xsl:for-each>

</xsl:template>

まとめ

結論として、同時発生制約を扱うとき(XMLレポートツールが必要な場合など)には、SchematronをXMLスキーマの機能強化として利用することができます。パフォーマンスの重要性が高い場合や、特にバリデーションの後に変換を行う場合には、XSLTとJava拡張機能を使った方がよりコンパクトになります。


ダウンロード

内容ファイル名サイズ
Java and XSLT source code used in this articleadvanced_xml_validation.zip18KB

参考文献

学ぶために

製品や技術を入手するために

  • Saxon: Saxon XSLTプロセッサーをダウンロードできます。
  • Xalan-J: Xalan-J XSLTプロセッサーをダウンロードできます。
  • Xerces: Xerces XMLパーサーをダウンロードできます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Java technology
ArticleID=240096
ArticleTitle=拡張XMLのバリデーション
publish-date=05092006