MSXML4.0を用いたXSLT変換

自作クラス

最終更新
2003-06-06T00:09:44+09:00

JtrXML

JtrXMLは、XML文書を表すクラスです。速度やメモリ効率を犠牲にして、扱いやすさを優先しました。主にXSLTによる変換とDOM level1 Core インターフェイスを利用したXML文書の操作をする為に用います。

JScript製なので所詮なんちゃっての域を出ませんが、HTAとの相性が良いのが特徴です。WSH5.6と併用すれば、保守性もそこそこ向上すると思います。*.wsfファイル自身をこのクラスで扱うと、中々面白いことになります。

以下はJtrXMLの概観です。

JtrXML
インスタンスフィールド
document
文書のDOMDocumentオブジェクト(IXMLDOMDocument2)
location
文書のパス(String)
インスタンスメソッド
getDocument
XPathを使用する際に必要なDOMDocumentオブジェクトを得る
getError
妥当性検証を行ってエラー(String)を得る(validならfalseを得る)
transform
XSLT変換を行い、変換後のXML文書(JtrXML2)を得る
transformAndSave
XSLT変換を行い、変換後のXMLソースコードを保存し(オプション)、ソースコード(Stirng)を得る
indent
XMLソースコードを無理矢理インデントする(インデント後のXMLにHTML互換性はありません)
save
保存する

コンストラクタは引数に指定されたパスの文書をパースし、well-formedではない場合に例外を投げます。例外というかErrorオブジェクトです。

JtrXMLクラスを継承した、JtrXSL、JtrXML2クラスも同梱されます。

JtrXSLクラスは、XSLT文書を表すクラスです。JtrXMLクラスを継承し、独自のメソッドを幾つか持ちます。

JtrXSL(継承:JtrXML)
インスタンスメソッド
setParam
パラメータを渡す
applyTo
自分自身を他のXML文書に適用して変換を行い、変換後のXML文書(JtrXML2)を得る
applyToAndSave
自分自身を他のXML文書に適用して変換を行い、変換後のXMLソースコードを保存し(オプション)、ソースコードを得る

JtrXML2クラスは、変換後のXML文書を表すクラスです。JtrXMLクラスを継承し、独自のフィールドを幾つか持ちます。

JtrXML2(継承:JtrXML)
インスタンスフィールド
transformer
自身を変換したXSLT文書(JtrXSL)
transformee
変換前のXML文書(JtrXML, JtrXML2 or JtrXSL)

まだ調整中なので詳細は後ほど。

JScript DOMサポート関数

最終更新
2003-02-08T18:22:46+09:00
相対URIを解決
String.prototype.resolveURI = function(_bURI)
{
	var rURI = this;
	if( /^[a-zA-Z]+:/.test(rURI) === true )
	{
		return rURI;
	}

	var ary = _bURI.match(/^([a-zA-Z]+):\/\/([\w\.]+)(\/.*)/);
	var sScheme = ary[1];
	var sAuthority = ary[2];
	var sPath = ary[3];

	if( /^\//.test(rURI) === true )
	{
		return sScheme + '://' + sAuthority + rURI;
	}

	sPath = sPath.replace(/[^\/]+$/, '');
	
	rURI = rURI.replace(/^\.\//, '');
	
	while( /^\.\.\//.test(rURI) === true && sPath.length)
	{
		rURI = rURI.replace(/^\.\.\//, '');
		sPath = sPath.replace(/[^/]+\/$/, '');
	}
	
	rURI = rURI.replace(/\.+\//g, '');

	return sScheme +'://'+ sAuthority + sPath + rURI;
}
innerText互換
function getInnerTextMS(node)
{
	var doc = node.ownerDocument;
	if(doc.getProperty('SelectionLanguage') !== 'XPath')
		doc.setProperty('SelectionLanguage', 'XPath');

	var nlTextNodes = node.selectNodes('descendant::text()');
	var aryTexts = new Array();
	for(var i = 0, len = nlTextNodes.length;
			i < len;
			i++)
	{
		aryTexts[i] = nlTextNodes.item(i).data;
	}
	return aryTexts.join('');
}

XSLT(XPath)関数の拡張

最終更新
2003-04-20T02:01:56+09:00

MSXML4では、独自のXPath関数を定義し、XSLTで使用することが出来ます。

練習1:ノードの文字列値にある文字列が含まれているか否か

あるカレントノードについて、そのstring-value(文字列値)に「2003-03-07」といった日付を表す文字列が含まれているかどうかによって、テンプレート内の処理を分岐したい場合があるとします。

しかし、実はかなり似たビルトイン関数があります。contains()関数です。次のテンプレートは、カレントノードの文字列値(string(self::*))に「2003-03-07」という文字列が含まれているときにのみ、xsl:if要素の内容がインスタンス化されます。


<xsl:template match="title">
 <xsl:if test="contains(string(self::*), '2003-03-07') = true()">
  ..
 </xsl:if>
</xsl:template>

ところで、「string(self::*)」は、「.」と記述することもできます。しかし、contains()の第一引数は「string」であって、「ノード集合」ではありません。第一引数にノード集合をstring型に変換するstring()関数が勝手に適用される点に注意が必要です。応用を利かせる際にこれを知らないと困るので、なるべくstring()関数は明示するようにしましょう。

さて、検証したいのは「2002-03-07」という文字列があるかどうかではなく、「\d{4}-\d{2}-\d{2}」といった正規表現で表される文字列があるかどうかでした。従って例えば、次のように使用できる関数が欲しいわけです。


<xsl:template match="title">
 <xsl:if test="my:contains(string(self::*), '\d{4}-\d{2}-\d{2}') = true()">
  ..
 </xsl:if>
</xsl:template>

このmy:contains関数を定義してみます。

まずJScriptで、第一引数に検索対象となる文字列、第二引数に検索文字列を表す正規表現文字列を要求し、マッチしたならtrue、しなければfalseを返却する関数を作ります。

function contains(_str, _sReg)
{
  var re = new RegExp(_sReg);
  return re.test(_str);
}
注意点
関数の戻り値はXPath 1.0で扱えるデータ型に従って「ノード集合(ノードリスト)、文字列、ブール値、数値」の何れかにします。

次に、作成したJScript関数を、msxsl:script要素の内容としてトップレベル(xsl:stylesheet要素の直下)に配置します。

<msxsl:script language="JScript" implements-prefix="my">
function contains(_str, _sReg) {
	var re = new RegExp(_sReg);
	return re.test(_str);
}
</msxsl:script>

msxsl:script要素のlanguage属性に使用するスクリプト言語を指定します。implements-prefix属性の値は、関数名として使用するQNameの名前空間接頭辞です。この接頭辞が関連付けられる名前空間をルート要素辺りで宣言します。

msxsl:script要素の接頭辞としてしようした「msxsl」は、名前空間「urn:schemas-microsoft-com:xslt」に関連付けられています。この名前空間に関連付けられていさえすれば「msxsl」でなくてもOKです。同様にルート要素辺りでこの関連性を宣言します。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="http://purl.org/jintrick/xpath-extension/"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="my msxsl"
>

<msxsl:script language="JScript" implements-prefix="my">
function contains(_str, _sReg) {
	var re = new RegExp(_sReg);
	return re.test(_str);
}
</msxsl:script>
</xsl:stylesheet>

「my」と「msxsl」は、変換結果では使用しない接頭辞ですから、exclude-result-prefix属性に列挙します。変換結果でもしようするのならこの限りではありません。

尚、myの名前空間URIは「http://purl.org/jintrick/xpath-extension/」としましたが、競合が無ければ何でも構いません。一応このURIを参照するとこの文書にリダイレクトされるようにしてありますので、変更しない方が良いかもしれません。