![]() ![]() ![]() ![]() XMLデータストリームの検証を動的プロセスにするこの記事のURLhttp://japan.internet.com/developer/20080429/26.html
著者:Kurt Cagle
海外internet.com発の記事
はじめにXML Schemaのもともとの目的は、XMLの検証および定義をDTD(文書型定義)よりも優れた方法で行うためのレイヤを提供することでしたが、その根本にあったいくつかの概念はいつのまにか変化しました。そうした概念の1つが、「属性は列挙(リスト)に設定できるが、その列挙はスキーマ自体の中に指定する必要がある」というものです。XMLが分散化するに従って、また、データ構造が複雑化して静的な定義だけでなく動的な定義にも依存するようになるに従って、開発者たちは、そうした動的な定義を自分自身でサポートするスキーマ言語を使う必要があると認識するようになりました。ISO SchematronはXPath 2.0(特にdoc()やunparsed-text()のような関数)を利用できるようになったため、ビジネスコンテンツのためのクリティカルな「分散検証」言語であると考えていいかもしれません。Schematronの詳細については後述しますが、私は以前、Nordstromという高級衣料品販売業者のためにコンサルタントとしてプログラミングを行っているときに、Schematronが非常に適していると思われる用途を色々思いつきました。 その頃、私はWeb経由のデプロイメントを通じて、仕入先から送られてくるインボイスを検証・表示・編集するメソッドを開発していました。当時はEDI標準が徐々に姿を消し、何らかの形式のXMLに向かっている時期でした(ebXMLが完成してまだ1年というところでした)。私はXMLに気持ちが傾いていたので、使えそうなツールをあれこれ引っ張り出してきて――新たに登場したXML Schemaもそこに含まれていました――XSD(XML Schema Difinition)を使ってインボイスのモデル化を始めました。ほとんどのインボイスは非常にすっきりとマッピングできました。若干難しかった点は、処理対象の店舗のIDを検証して正しいことを確認する必要があったことです。 この問題は簡単に解決できそうに見えるかもしれません。単に、一連の店舗をスキーマの<xs:enumeration>要素で列挙すればいいだけの話ではないのでしょうか? しかし、Nordstromはそのころ整理統合を進めている最中で、毎週のように店舗が閉鎖されていました。正規表現と比較することにすれば、そうした要件が要らなくなると思うかもしれません。しかし、それではうまくいきません。検証をする大事な理由の1つは、問題の店舗が正当なものであり、なおかつ閉店していないことを確認することだからです。結局開発したのはその場しのぎのソリューションでした。つまり、サーバー自身がデータベース呼び出しから直接取得したリストを使ってXMLを後処理するという方法です。 それからしばらく経ったのち、スキーマで生成されたXFormsを扱っているときに、同じ問題がまた浮かび上がってきました。そこで、リストの性質とリストとモデルとの関係について色々と考えました。最も根本的なところでは、すべてのリストには詰まるところ2つの共通点があります。1つは、リストはオブジェクトのシーケンスであるということです。もう1つはもっと重要で、リストの各オブジェクトには、それに対する一意のキーがあって、それがリスト内でオブジェクトを識別しているということです。インデックス化された領域の場合、キーとはアイテムの位置を識別するための番号です(通常は0または1がベースになります)。したがってアイテムの名前(キー)が一意なのはその配列(リニアな配列)のコンテキスト内に限られ、その配列において、その番号に至るまでのシーケンスが変わらない場合だけです。 たとえば、次のような色リストのサンプルで考えてみましょう。 colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet'] colors = {0 : 'red', 1 : 'orange', 2 : 'yellow',
3 : 'green', 4 : 'blue', 5 : 'violet'}
print colors[0] => red colors[0] = 'scarlet' print colors[0] =>scarlet colors = {rd : 'red', or : 'orange', ye : 'yellow',
gn : 'green', bu : 'blue', vi : 'violet'}
print colors['rd'] => red colors['rd'] = 'scarlet' print colors['rd'] => scarlet 連想配列伝統的なHTMLページを見てみると、<select>要素は通常、連想配列の使用を前提として機能しています。colors = {rd : 'red', or : 'orange', ye : 'yellow',
gn : 'green', bu : 'blue', vi : 'violet'}
<select value="rd" name="colorkey">
<option value="rd">red</option>
<option value="or">orange</option>
<option value="ye">yellow</option>
<option value="gn">green</option>
<option value="bu">blue</option>
<option value="vi">violet</option>
</select>
<xf:select1 ref="colorkey"> <xf:item> <xf:label>red</xf:label> <xf:value>rd</xf:value> </xf:item> <xf:model id="datamodel"> <xf:instance id="data"> <data> <colorkey>rd</colorkey> </data> </xf:instance> <xf:instance id="colors"> <colors> <color name="rd" label="red"/> <color name="or" label="orange"/> <color name="ye" label="yellow"/> <color name="gn" label="green"/> <color name="bu" label="blue"/> <color name="vi" label="violet"/> </colors> </xf:instance> </xf:model> <xf:select1 ref="colorkey"> <xf:itemset nodeset="instance('colors')//color"> <xf:label ref="@label"/> <xf:value ref="@name"/> </xf:item> </xf:select1> ここまではよいでしょう。列挙されたリストを使ったスキーマは、スキーマ(特にXSDスキーマ)の使用法としてはごく一般的です。ではさらに進んで、制約リストを外部XMLファイルに移し、そこから取得するという方法を考えてみましょう。 <xf:model id="datamodel"> <xf:instance id="data"> <data> <colorkey>rd</colorkey> </data> </xf:instance> <xf:instance id="colors" src="colors.xml"/> </xf:model> <xf:select1 ref="colorkey"> <xf:itemset nodeset="instance('colors')//color"> <xf:label ref="@label"/> <xf:value ref="@name"/> </xf:item> </xf:select1> では、もう一歩進んでみましょう。 <xf:model id="datamodel"> <xf:instance id="data"> <data> <colorkey>rd</colorkey> </data> </xf:instance> <xf:instance id="colors" src="''colors.xq''"/> </xf:model> <xf:select1 ref="colorkey"> <xf:itemset nodeset="instance('colors')//color"> <xf:label ref="@label"/> <xf:value ref="@name"/> </xf:item> </xf:select1> 例が不自然に思えるかもしれませんが、これは前述したNordstromの店舗の問題とまったく同じであり、実際にWebアプリケーションの構築でよく見られるようになっている問題です。では、ただの空白テキストフィールドと制約のないセットにするべきなのかというと、そうではありません。このようなセットは未知のものではなく、いつでも検証可能ですが、動的な性質も備えているということです。このような検証は時間によって結果が変わってきます。その色は使えるかどうか、その店舗は営業中かどうか(閉まっていないか)という検証を行うには、時間をパラメータとして考慮しなければなりません。実世界の物事はこのように動いており、この事実を便宜上の都合で無視すると、結局はうまく機能しないモデル(およびアプリケーション)ができてしまいます。 読み取り専用Webサービスのシナリオこのようなパラメータ化ならうんざりするぐらいお馴染みだと思うならば、それは、Webサービスを使っているときに実際に同じ問題が起きているということです。とりあえず、HTTP GETプロトコルに基づいた読み取り専用Webサービスだけを考えてみましょう。読み取り専用Webサービスが使われそうな2つの異なるシナリオを取り上げます。1つ目のシチュエーションは、何らかの理由で問題のモデルをクライアント環境内に直接簡単に収めることができない場合です(データベースにホストされているとか、セキュリティの権限が絡んでいるなど)。こうしたサービスを「便宜的なサービス」と呼ぶことにします。理論的には情報をローカルにホストすることもできますが、そうするのは効率的ではありません。このケースでは、データ環境は基本的に静的です。同じ呼び出しを異なる時期に2回実行した場合、時間以外のパラメータが同じなら、2つの呼び出しは同じコンテンツを返します。このような呼び出しのスキーマを作成することは理論的に可能で、このスキーマには特定の値のセットが含まれることになります(大きくなる可能性はあります)。このアプローチの良い例が、郵便番号を地域の特定の区域にマッピングする郵便番号簿でしょう。郵便番号簿は変更される可能性がありますが、モデル作成者が気にしなければならないほど頻繁に変更されるものではありません。 しかし、もう1つのシチュエーションはかなり興味深いものです。このケースはサービス自体が動的な環境を扱っています。たとえば、典型的なWebサービスとして、1日の始まりから現在の時間までの普通株の変動(+/?で差分を通知するようなもの)を取得するものを取り上げてみましょう。ポイントだけ説明すると、このサービスは、前回通知した期間以降に値が増加した特定の株のセットを一覧で表示します。この場合のタクソノミーは機能的かつ動的です。これをラジオボタンのリストとして実装し、個々の株をラジオボタンで表すならば、Webサービスをリフレッシュするたびにボタンの数やボタンのコンテンツが変化することになるでしょう。 これはXFormsに限ったことではありません。実際、AJAX(Asynchronous JavaScript and XML)全般に見られるもっとたちの悪い問題の1つは、データモデルが拡散して分散するほど、データモデルのインスタンスを検証するのがますます難しくなるということです。そのため、オブジェクト指向プログラミングでもXMLでも、これまで考案されてきた検証の概念の大部分は役に立ちません。 さて、ここで重大な疑問が出てきます。果たして検証は必要なのでしょうか? たとえ完全に信頼できるネットワークでも、「何らかの形態の検証は必要」という答えになるでしょう。そのようなネットワークでも、どこかの時点でXML(または関連するシリアライズされたコンテンツ)を作成する必要があり、そのXMLの作成プロセスに欠陥がある可能性があります。しかし、それにまつわる検証はむしろ包括的なユニットテストに組み込まれています。箱に封をし、この閉じたシステム内のコンテンツは有効で一貫性があると確定した後では、エラーの原因となるものはモデル自体の欠陥から来るものとしか考えられず、それは当然ながら検証では解決できません(そのような検証はモデルの一部です)。 しかし、XMLコンテンツが環境の外側から入ってくる可能性を作った時点で、検証は困難になります。また、XMLの主な役割が異種のシステム間でメッセージをやり取りするフォーマットであるため、システムに入ってくるコンテンツが内部的に一貫性がありかつ正当なものであることを判断する何らかの方法が必要になるでしょう。 しかし、静的なスキーマ言語は、せいぜい構造型または基本型の検証を提供できるに過ぎません。それでさえ、モデルが複雑になるに従って、そうしたスキーマが適切にコンテンツを検証できる可能性は運次第なものになります。モデルの外側に存在するタクソノミー情報は、とりわけ動的なコンテキストでは、検証することができません。さらに、メッセージの正当性は検証できません。 1つ考えられるソリューションは、SOAP/WSDLの相互交換に特化した複雑なWebサービスのインフラストラクチャをセットアップし、識別管理のための連携システムを確立し、すべてを暗号化されたまとまりとして扱い、関係するすべてのシステムにまたがるハンドシェークメカニズムを本格的に構築して、インターネットという基本的に信頼できないネットワークを、閉じたプライベートな完全に信頼できるネットワークに変えることです。このアプローチは、ほとんどのWS-*イニシアチブの作成に大いに役立ちます。 見かけより単純なSchematron安全性には劣りますが、もっとシンプルなシステムにする方法もあります。それは、検証メカニズムに、スキーマファイルの外部のリソースと対話するだけのインテリジェンスを組み込むことです。実は、このアプローチはISO Schematronが採用しているものです。Schematronの背後にある考え方は見かけより単純です。Schematronドキュメントは、XMLで書かれたルールのコレクションから成り、各ルールはXPath式で表される特定のコンテキストを操作します。各ルール内には、コンテキストに関する特定の条件をテストする一連のアサーション(それ自体がXPath式と述語から成ります)が含まれます。条件が真なら何も起こりませんが、偽であればSchematronは特定のテキストまたはXHTMLフォーマットでメッセージを返します。カスタムのパーサーを使うこともできますが、ほとんどの一般的なSchematronのプロセスは次のように実行されます。
document()関数は引数を2つ受け取ります。1番目の引数はURL文字列またはURLのノードセットで(2.0ではシーケンスの場合もあり)、2番目の引数はドキュメントのコンテキストです(通常は現在のノードの参照を引数の値として受け取ります)。この関数の結果は、渡されたURLから得られる1つ以上のドキュメントです。注目すべきは、これらのURL自体がパラメトリックなGETベースのWebサービスである場合、それに基づいて外部のサービスからコンテンツを取得して、動的なタクソノミーからコンテンツを検証できるということです。 たとえば、1つのパラメータ(colorkey)を受け取るWebサービスがあり、そこから次のような形式のXMLノードが返されるものとします。 <color name="rd" label="Red" status="200" statusMessage="Color is valid."/> <color name="rd" label="Carmine" status="500" statusMessage="Color rd was found, but has been retired."/> <color name="rd" label="(unknown)" status="400" statusMessage="Color 'rd' was not found."/> <schema xmlns=http://purl.oclc.org/dsdl/schematron>
<pattern id="confirmTaxonomies">
<rule context="colorkey">
<let name="$keyValue" value="."/>
<let name="colorDoc"
value="document(concat('colors.xq?colorkey=',$keyValue),.)"/>
<assert test="$colorDoc[@status=200]">
<value-of select="$colorDoc[@statusMessage]"/>
</assert>
</rule>
</pattern>
</schema>
XMLデータストリームを扱う人は、ここで紹介したSchematronのようなものを、分散コンテキストでも正常に動作する宣言スキーマとして使うアプローチをもっと詳細に研究しなければなりません。ネットワークでの接続がますます進む環境では、検証自体も「グローバル化」する必要があり、よりいっそう機能性を高め、新しい処理モデルに移行していく必要があります。もはや、XMLコンテンツを単一の静的なドキュメントで記述できた時代は過ぎ去ろうとしているのです。 著者紹介Kurt Cagle(Kurt Cagle)
ライター、情報アーキテクト、XML News NetworkとMetaphorical Webのウェブマスター。カナダ、ブリティッシュコロンビア州のビクトリア在住。
|