XSLTの最近のブログ記事

概要: XSLT 1.0の言う所の結果ツリーフラグメントを使わないように、XSLT文書を書き直しました。今まで結果ツリーフラグメントをノードセットに再評価してxsl:for-eachをかけていた所はすべて、デ...

XSLT 1.0の言う所の結果ツリーフラグメントを使わないように、XSLT文書を書き直しました。今まで結果ツリーフラグメントをノードセットに再評価してxsl:for-eachをかけていた所はすべて、デリミタで分けられた文字列を再帰で順番に処理するようになりました。どちらがより負荷が大きいのか判りませんが、何れにせよこの程度なら大したものではなかったようです。

さて、このように書き直した事で、IE 6.0以降やSablotronだけでなく、libxsltやFirefox 2.0以降でも変換できるようになった為、Firefox 2.0以降の場合も変換をクライアントに委譲する事にしました。そう言えばOperaもdocument関数が実装されるんだったな、と思って調べてみた所、Opera 9.50から正式に実装されていたようです。これならOperaでも丸投げできる、と思っていた所、案の定残念な事にOperaではdocument関数がうまく機能していないようです。

はて、こんな事でOpera 9.50のChangelogAdded support for XSLT document() function.などと載せるものだろうか......と思い、少々実験した所、次の事が判りました。

XSLT文書と同階層のelements.xmlに対して、document( 'elements.xml' )
問題無く読めます。
XSLT文書の親階層のelements.xmlに対して、document( '../elements.xml' )
問題ありません。
XSLT文書の親階層のdataディレクトリの中のelements.xmlに対して、document( '../data/elements.xml' )
読めません
/reference/xhtml11/dataのelements.xmlに対して、document( '/reference/xhtml11/data/elements.xml' )
勿論読めません

......何この不思議実装。Operaの開発チームの人達は、スタイルシートとデータを別のディレクトリに置くって事を誰一人しなかったのか。それともこんな事が起こるのは僕の所だけ?

document関数が失敗する事によって変換すべてが失敗する事はありませんが、document関数が空のノードセットしか返してくれないので、結局document関数は使えません。これはdocument関数の振る舞いがおかしいと言うよりは、単にフルパスの解決に失敗しているだけのように思えるので、もしバグであれば簡単に直りそうですが......どこに言えばいいんだろ。ちょっと探してみるか。

追記: レポートの為にもう少し詳しく現象を調べようとした所、どうやらdocument関数の戻り値が必要になった時点でGETリクエストは送っている模様。しかし戻り値は空のまま。というより、XSLTプロセッサ自体がその時々で挙動が変わったりして(何に依存しているのかはもう調べるの面倒)、不安定。ひょっとしたら根の深い問題なのかも知れない。やっぱりOperaは保留。

概要: 今手許で使えそうなXSLTプロセッサ。XSLT 2.0をサポートするXSLTプロセッサは、Java VM上で動くSaxonだけしか今の所無いらしいので(知らないだけかも)、多分すべてXSLT 1.0に...

今手許で使えそうなXSLTプロセッサ。XSLT 2.0をサポートするXSLTプロセッサは、Java VM上で動くSaxonだけしか今の所無いらしいので(知らないだけかも)、多分すべてXSLT 1.0に準拠しているはず。従って、xsl:paramxsl:variable要素の内容はResult Tree Fragmentsになります(ここではノードセットと結果ツリーフラグメントで書きました)。

で、例によって、XSLT 1.0では結果ツリーフラグメントに対して文字列操作しか許されないんですが、WWWWARDで使っているXSLT文書では結果ツリーフラグメントをノードセットに評価し直すという(XSLT 1.0勧告には無い、各実装の独自拡張の)機能を使っているんですね。別にこれはそういう機能がある事を知ってから使い始めた訳ではなく、「こう書けると綺麗だ」と思って書いたら使えて、あとで確認してみると独自拡張だったという......(あんまりよろしくない経緯)。それはともかく、IEに搭載されているXSLTプロセッサや、PHP 4のSablotronではこれが使えます。しかし、libxsltでは使えない。無論XSLT 1.0でこれをするのは邪道なので使えないのがあるべき姿なのかも知れませんけど......。

現在s26.xrea.comサーバに入っているPHPのバージョンは4.4.8だそうです。Sablotronも使えるようになっています。よって今は問題無いんですが、PHP4のサポートは2007-12-31に終わっています。何れPHP5が取って代わるでしょうが、そのPHP5はSablotronを標準装備していません(PECLに移されたって書いてあるけど、見つからない)。代わりにlibxsltが手厚くサポートされているようですが、前述の通り、libxsltはこの「結果ツリーフラグメントのノードセットとしての再評価」を許していないようです(そういう事をしようとすると、それより深い所の変換がすべてキャンセルされる)。

行くべき道は......

  • ほっとく。これは楽で良い。......じゃなくて。
  • XSLT文書を書き直す。XSLT 1.0の機能のみをサポートするプロセッサで変換できるように。元々結果ツリーフラグメントの再評価なんてやりだしたのは、綺麗に書けるからであって、必要不可欠だったからではなかった、はず......(うろ覚え)。尤も単なる文字列処理だけでやろうとすると、若干トリッキーになるか、再帰が深くなるかしそうだけど、許容範囲かな。
  • XSLT 2.0プロセッサ全盛時代の到来を待つ。テンポラリツリーならノードセット操作も問題無し。唯一の問題は、それがいつ来るのか。ひょっとしてもう来てたりして(時事に疎い)。......無いか。夢か。

という訳で、今の内に書き直すべきでしょうか。PHP5+libxsltで変換できると、ローカルマシンでFirefoxを使って変換結果確認ができるので若干幸せなんですよね。若干。

さあ、それは良いとしても......libxsltって、xsl:templatepriority属性は無視なんですかね? てっきりlibxsltではxsl:importが使えないと思っていた時(今さっき)に、xsl:includeを使ってなんとかしようと思ったら、priorityが違っても"duplicate name!"って怒られたのですが......いや、これはさっきまでテンプレートルールの衝突の解決方法を知らなかったなんちゃってXSLTユーザの苦情じゃないですね、はい。

概要: げげげぇ(何て汚い言葉遣いなんだ)。XSLT 2.0とXPath 2.0が2007-01-23に勧告されていますよ! W3Cの新着情報はRSSでSyndicateしてたのに、見落とすとは……。何の為に...

げげげぇ(何て汚い言葉遣いなんだ)。XSLT 2.0XPath 2.0が2007-01-23に勧告されていますよ! W3Cの新着情報はRSSでSyndicateしてたのに、見落とすとは……。何の為にチェックしてるんだか……はぁ。

ま、それはともかく待望のXSLT 2.0とXPath 2.0ですね。風の便りに聞いた所によると、XPathでは結果ツリーフラグメントをノードセットに再評価する事が仕様で定められるとの事でしたが、さて……

ほう、結果ツリーフラグメント(Result Tree Fragment)は消えたんですね(The result tree fragment data-type is eliminated.)。変数結合要素(と訳すのか? Variable-binding Element。xsl:variable要素とxsl:param要素の事)の値は、全部テンポラリツリーになるようですね(A variable-binding element with content (and no as attribute) now constructs a temporary tree, and the value of the variable is the root node of this tree.)。テンポラリツリー(Temporary Tree)はソースツリーでも最終結果ツリーでもない全てのツリーで、ソースツリー(Source Tree)というのはXSLT変換の入力で与えられるツリー(*.xmlファイルなんかで記述するツリーの事ですね)、最終結果ツリー(Final Result Tree)というのは最終出力にそのまま吐かれるツリーの事のようです。そして、テンポラリツリーはノードセットとしても扱える! いやあ、素晴らしい。これで、仕様書も言っているように独自実装のnode-set関数は消え去るでしょう。そして、MozillaやOperaでも僕の望むようなXSLT変換ができるようになるでしょう! ……って、Operaはまだdocument関数すら実装されていないんでした。……今、Opera 9.10にバージョンアップしてみましたが、Changes logにも書いてないし、実験しても駄目でした。

XSLT 2.0とXPath 2.0に対応するユーザーエージェントはいつ頃登場するでしょうか。IEが一番早そうですかね。今から楽しみです。

概要: Opera 9もXSLTプロセッサが実装されたようですが、Mozilla Firefoxでも実装されているんですね。Netscapeの日本法人が撤退してバージョンアップが止まっているということを(今更...

Opera 9もXSLTプロセッサが実装されたようですが、Mozilla Firefoxでも実装されているんですね。Netscapeの日本法人が撤退してバージョンアップが止まっているということを(今更)知ったので、Firefox 2.0を入れてみましたが、…うん、これはなかなか。フォント周りも強くなってるし。

Firefox 2のXSLTプロセッサはXSLT 1.0のdocument関数が使えるのはいいんですが、結果ツリーフラグメント(Result Tree Fragment)をノードセットとして評価することができないようです。

そもそも結果ツリーフラグメントって何かというと、大雑把に言えばxsl:variable要素やxsl:param要素の内容としての値です。例えば、

<xsl:variable name="result">
  <level>3</level>
  <number>1-1</number>
</xsl:variable>

上記のような変数resultがあった場合、$resultはlevelとnumberの二つのノードを持つノードセットのように見えますが、これはノードセットにはなりません。結果ツリーフラグメントと呼ばれます。結果ツリーフラグメントに対しては文字列操作しか許されず、勿論/や//、[]などのノードセットに対してのみ使える演算子は使えません。ただ、xsl:copy-of要素のselect属性で結果ツリーフラグメントを値に持つ変数を指定すると(<xsl:copy-of select="$result"/>のようにすると)、あたかもそれがノードセットであるかのように振る舞います。

別の例を見てみましょう。対象とするXML文書の子孫にbodyという要素があって、body要素は幾つかのsection要素を子に持っているとします(うちのXML文書のスタイルです)。この時、次のXSLTコード(断片というべき?)は許されます:

<xsl:variable name="body" select="//body"/>
<xsl:for-each select="$body/section">
  <!-- 何らかの処理 -->
</xsl:for-each>

しかし、次のようにすると誤りです。

<xsl:variable name="body">
  <xsl:copy-of select="//body">
</xsl:variable>
<xsl:for-each select="$body/section">
  <!-- 何らかの処理 -->
</xsl:for-each>

先程も言った通り、xsl:variable要素などの内容にしてしまうと、それがなんであれ結果ツリーフラグメントになるのです。数値を書いても文字列を書いても何でもかんでも結果ツリーフラグメントに。一方で、select属性で値を与えると、式の型と同じになります。ノードセットならノードセットに、数値なら数値に、文字列なら文字列に、ブーリアンならブーリアンに。そんな訳で、前者の$bodyはノードセットで、後者の$bodyは結果ツリーフラグメントなのです。ああ無情。

しかしながら、xsl:variableの内容として書いてもノードセットとしてみてもらいたいことは少なくないわけで、──例えば変数の値にa要素を幾つか書いておいて、あとでそれをxsl:for-eachで処理するとか、テンプレートの戻り値(戻り結果ツリーフラグメント?)が複雑になる場合、さっきのlevelやnumberのように要素で区切って返すとか(Cで言うと構造体を返す感じ)。そんな時のために、IEやSablotron(PHPとかでも使える、何かの素粒子か加速器みたいな名前のXSLTプロセッサ)などでは次のような技巧が使えます:

<xsl:variable name="result">
  <level>3</level>
  <number>1-1</number>
</xsl:variable>
<xsl:variable name="result-node" select="$result"/>

これで、$resultは結果ツリーフラグメントですが、$result-nodeは(select属性で値を与えているので)ノードセットになります。……って、こんなんでいいなら最初っからxsl:variableの内容でもノードセットにしろッ! と叫びたくなりますが、まぁXSLT 1.0の仕様書に「内容だったら結果ツリーフラグメントと見なせ」って書いてあるので、なんとも……。ちなみに独自拡張のnode-set関数でもいいようです。

IE 6でもこの技巧が使えたので、数年前からずっとこの方法を愛用しているのですが、どーもFirefoxのXSLTプロセッサはこれが駄目らしい。仕様書に書いてないからバグではないんだけど、悲しい。Opera 9はこの方法が使えるけど、document関数がまだ実装されてないし…(近々実装されるという噂だけはあるけども)。うーん。結局IE6以降でなければサーバーサイドで変換するしかないようで。はぁ、がっくし……

このアーカイブについて

このページには、過去に書かれたブログ記事のうちXSLTカテゴリに属しているものが含まれています。

前のカテゴリはXHTMLです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。