単一のXML Schema文書で複数のXML文書を検証するための実用化に向けて動いてみました。
一番の問題点と言えば、ルート要素にxsi:schemaLocation属性(名前空間を持っている場合)あるいはxsi:noNamespaceSchemaLocation属性(名前空間を持っていない場合)を記述して、スキーマ文書へのリンクを作成しなければならない点です。より正確には、それらの属性を記述することによってパーサがスキーマ文書を必ず読み込んでしまうのが問題です。
パーサが賢ければ問題にはならない筈ですが、残念ながらMSXML君は自動でキャッシュを利用してくれることは無いようです。それどころか、standalone="yes"
と宣言しても、あるいは、validateOnParse = false;
と設定しても、ご丁寧に長大なスキーマ文書を読み込んでしまうようです。
どうしようもないので、先に挙げたxsi:schemaLocation属性やxsi:noNamespaceSchemaLocation属性を記述せずに、任意のスキーマ文書でそのXML文書を検証する方法を探ってみました。
MSXML4にはXMLSchemaCacheというものが用意されていました。
まずこのようにしてオブジェクトを作成します。
var cache = new ActiveXObject("Msxml2.XMLSchemaCache.4.0");
ここまでは良いのですが、私がやりたいのは任意のスキーマ文書をこの「キャッシュ」に含めることです。従って、参照先の例に挙げられているaddCollectionメソッドは使えません(結局例の長ったらしい属性を記述しなければならない)。addメソッドが使えます。
cache.add('', 'C:\\schema\\hub-jtr.xsd');
第一引数に名前空間URI(string)、第二引数にスキーマ文書のパス(string)を指定します。名前空間を持たない場合、第一引数は空文字列でOKなようです。
で、このキャッシュを検証したいXML文書に関連付けるにはどうするかというと、DOMDocumentオブジェクトのschemasプロパティにこのキャッシュを「代入」すれば良いらしいです。 甚だ不正確な表現です。正しくは、「IXMLDOMDocument2オブジェクトのschemasプロパティを、このキャッシュへの参照に設定する」と書けば良いのでしょうか。ややこしい表現です。
var doc = new ActiveXObject('MSXML2.DOMDocument.4.0');
doc.async = false;
doc.load('C:\\Documents\\index.xml');
doc.schemas = cache;
IXMLDOMDocument2オブジェクトのvalidateメソッドで実際に検証できます。このメソッドはIXMLDOMParseErrorオブジェクトを返却しますが、loadメソッドが完了した時点で作成されるIXMLDOMParseErrorオブジェクト(この例ではdoc.parseErrorで参照可能)が更新されるわけではない点に注意が必要かもしれません。私はここで引っかかりました。
var err = doc.validate();
if(err.errorCode !== 0)
alert(err.line + ' : ' + err.reason);
これで、「118 : DTDまたはスキーマによると要素の内容が正しくありません」なんてなメッセージを得られるわけです。
面倒くさいので、自作のgetErrorメソッドの引数にスキーマのキャッシュを指定する形で同様の結果を得られるよう、以上のようなプロセスを纏めてみました。しばらく使い勝手を試してみます。
なんで最近こんな時間(深夜3時)に更新するのかといえば、@NetHomeのFTPサーバが亀だからです。所謂「てれほたいむ」が始まった辺りから数時間、putしづらくて仕方なくなるんです。フリーのFTPソフトを使おうがDOS窓でFTP.EXEを使おうが、IEを使おうが同じです。夜な夜な巨大なバイナリデータを延々とputし続ける輩が存在するのでしょうか。勝手に想像して怒りに燃えています。