テンプレートを定義していない要素をそのまま出力
はじめに
xmlの要素に対応するテンプレートがxsltにある場合だけテンプレートを適応し,対応するテンプレートがない場合xmlの要素をそのまま出力してくれると便利である.そういうテンプレートを考えてみた.
環境は以下の通り.
以下試行錯誤の結果をつらつら書いている.結果を知りたい場合は最終結果へ.
テスト用xml
テスト用に以下のxmlを使用する.
sam.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment1 -->
<root>
begin
<!-- comment2 -->
<ul class="ul_attr" id="ul_id">
<li>foo<date>2008.01.29</date></li>
<li>bar<date>2008.01.30</date></li>
</ul>
end
</root>
以下の要素を含んでいる.
- 属性のない要素
- root,li
- 属性のある要素
- ul
- xsltで変換する要素
- date
- テキストノード
- begin,end,foo,bar,2008.01.29,2008.01.30
- コメント
- comment1,comment2
これを以下のように変換(date要素はspanに変換.それ以外はそのままコピー)するxsltを作ろう.
変換結果:
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment1 -->
<root>
begin
<!-- comment2 -->
<ul class="ul_attr" id="ul_id">
<li>foo<span class="date">2008.01.29</span></li>
<li>bar<span class="date">2008.01.30</span></li>
</ul>
end
</root>
テンプレートを何も定義しないxslt
テンプレートを何も定義しない下記のxsltで上記sam.xmlを変換してみよう.
sam.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
</xsl:stylesheet>
コマンドライン(Cygwin版bash)から以下のように実行する.なお,Saxonをインストールしたディレクトリを$saxon_homeと表記する.
java -jar "${saxon_home}/saxon9.jar" -versionmsg:off -s:sam.xml -xsl:sam.xsl
こんな結果になる.
実行結果:
<?xml version="1.0" encoding="UTF-8"?>
begin
foo2008.01.29
bar2008.01.30
end
テンプレートを何も定義しない場合,デフォルトで以下の規則が有効になるからである.
- 要素ノード
- xsl:apply-templatesを呼ぶ
- テキストノード
- そのまま出力
- コメントノード
- 出力しない
したがって,sam.xmlにある
- 要素ノード(root,li,ul,date)
- 合致するテンプレートがなく出力しない.
- テキストノード(ulのclassとid)
- そのまま出力
- コメントノード(comment1,comment2)
- 出力しない
となる.
コピーはできる
単純にコピーするだけだったら,以下のxsltでできる.
sam.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="node()">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
しかし,コピーしているだけなので,xsltで変換する要素(date)のテンプレートを定義しても変換はできない.つまり,以下のxsltでsam.xmlを変換しても要素dateはspanにはならない.任意のノード(node())にマッチするテンプレートの中でxsl:apply-templatesを呼んでいないためである.
sam.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="date">
<span class="date"><xsl:value-of select="."/></span> <!-- ここのテンプレートはマッチしない -->
</xsl:template>
<xsl:template match="node()">
<xsl:copy-of select="."/> <!-- xsl:apply-templatesを呼んでないので -->
</xsl:template>
</xsl:stylesheet>
実行結果:
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment1 -->
<root>
begin
<!-- comment2 -->
<ul class="ul_attr" id="ul_id">
<li>foo<date>2008.01.29</date></li>
<li>bar<date>2008.01.30</date></li>
</ul>
end
</root>
apply-templatesを呼ぶ
「カレントノードをそのままコピーして,子ノードにテンプレートを適応するためにapply-templatesを呼ぶ」ようなテンプレートを作れば良いようだ.ということで考えたのが以下のxsltである.
sam.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="date">
<span class="date"><xsl:value-of select="."/></span>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}"> <!-- マッチしたのと同じ名前のノードを作って -->
<xsl:apply-templates select="node()|@*"/> <!-- ノード or 属性のテンプレートを呼ぶ -->
</xsl:element>
</xsl:template>
<xsl:template match="@*|comment()"> <!-- 属性とコメントはそのままコピー -->
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
実行結果:
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment1 -->
<root>
begin
<!-- comment2 -->
<ul class="ul_attr" id="ul_id">
<li>foo<span class="date">2008.01.29</span></li>
<li>bar<span class="date">2008.01.30</span></li>
</ul>
end
</root>
想定した内容になった.
copyで囲む
<xsl:copy/>
を使うとノードのコピーができる.これを使ってみよう.
sam.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="date">
<span class="date"><xsl:value-of select="."/></span>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- xsl:copyでコピーできるので,このテンプレートは不要
<xsl:template match="@*|comment()">
<xsl:copy-of select="."/>
</xsl:template>
-->
</xsl:stylesheet>
実行結果:
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment1 -->
<root>
begin
<!-- comment2 -->
<ul class="ul_attr" id="ul_id">
<li>foo<span class="date">2008.01.29</span></li>
<li>bar<span class="date">2008.01.30</span></li>
</ul>
end
</root>
最終結果
ということで,最終結果は以下の通り.
sam.xml(変換元):
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment1 -->
<root>
begin
<!-- comment2 -->
<ul class="ul_attr" id="ul_id">
<li>foo<date>2008.01.29</date></li>
<li>bar<date>2008.01.30</date></li>
</ul>
end
</root>
sam.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="date">
<span class="date"><xsl:value-of select="."/></span>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
実行方法:
$ java -jar "${saxon_home}/saxon9.jar" -versionmsg:off -s:sam.xml -xsl:sam.xsl
実行結果:
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment1 -->
<root>
begin
<!-- comment2 -->
<ul class="ul_attr" id="ul_id">
<li>foo<span class="date">2008.01.29</span></li>
<li>bar<span class="date">2008.01.30</span></li>
</ul>
end
</root>
変換元のコメントは変換結果に出力しないxsltは以下の通り.
sam.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="date">
<span class="date"><xsl:value-of select="."/></span>
</xsl:template>
<xsl:template match="*|@*|text()"> <!-- コメント要素以外にマッチ -->
<xsl:copy>
<xsl:apply-templates select="*|@*|text()"/> <!-- コメント要素以外に適応 -->
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
実行結果:
<?xml version="1.0" encoding="UTF-8"?>
<root>
begin
<ul class="ul_attr" id="ul_id">
<li>foo<span class="date">2008.01.29</span></li>
<li>bar<span class="date">2008.01.30</span></li>
</ul>
end
</root>
参考文献
Steven Holzner著/株式会社クイック訳,XSLT実践ガイド,株式会社アスキー