XPath2.0で採用される新たな機能の多くは、ちょっとしたコツと冗長になるであろうソースを記述する根気さえあれば、XPath1.0+XSLT1.0でも実現する事が可能である(無論、実現できない機能もあるだろう)。しかしながら、前者の最大の利点はXSLTなどの他の技術を使う事無く、XPathのみにおいて実現できるというところにある。
XPath2.0を勉強していて、自分的に最も便利と思える機能が、このif式だと思う。名前から想像できる通り、条件によって別々の式の値が取り出す事が出来るのである。説明するよりも、例を見てもらった方が分かり易いと思う。
<?xml version="1.0" encoding="UTF-8"?>
<doc>
<person>
<name xml:lang="en">Michael Douglas</name>
</person>
<person>
<name xml:lang="en">Andy Garcia</name>
</person>
<person>
<name xml:lang="en">Ken Takakura</name>
<name xml:lang="ja">高倉 健</name>
</person>
<person>
<name xml:lang="en">Yusaku Matsuda</name>
<name xml:lang="ja">松田 優作</name>
</person>
</doc>
上例から、日本名が併記されている場合は日本名で、それ以外の場合は最初の名前を取り出したいとする。XSLT1.0ならば、下記の様になるだろう。
【XSLT1.0】
<xsl:template match="person">
<name>
<xsl:choose>
<xsl:when test="name[lang('ja')]">
<xsl:value-of select="name[lang('ja')]"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name[position() = 1]"/>
</xsl:otherwise>
</xsl:choose>
</name>
</xsl:template>
【出力】
<result>
<name>Michael Douglas</name>
<name>Andy Garcia</name>
<name>高倉 健</name>
<name>松田 優作</name>
</result>
XPath2.0を使えば、これと同じ出力を得るのに、XPathの範囲内だけで可能になります。
【XSLT2.0】
<xsl:template match="person">
<name>
<xsl:value-of select="if (name[lang('ja')]) then name[lang('ja')] else name[position() = 1]"/>
</name>
</xsl:template>
if式の書き方は、if (テスト式) then 式1 else 式2
と言う形になります。テスト式を判定し、真になるようなら式1を、偽になるようなら式2を評価し、それがif式の値になります。上例で言えば、person要素がxml:lang="ja"
であるname要素を持っていればそのname要素を、持っていなければ最初のname要素が返ってくるという訳です。
then、elseに置けるのは式ですので、無論if式も置く事が出来ます。if式のネストを用いれば、より複雑な条件判断が出来るようになります。また、if式はifからelseまでがワンセットですので、真になる条件のみが必要な場合でもelse以下を省略する事は出来ません。そのように用いる場合は、elseの式に空シーケンスに与えます。
2chの「XSL/XSLT」スレを眺めていたら、XPath1.0の範囲内だけでも上例と同じ出力を得られる方法が提示されていた。xsl:choose
以下を、<xsl:value-of select="name[not(../name[lang('ja')])][position() = 1] | name[lang('ja')]"/>
にするのである(2ch「XSL/XSLT」296より、拝借)。
|
演算子の左の式の最初の述語で、自身の親要素が、xml:lang="ja"
である子要素nameを持っているか否かを選別し、その逆のboolean値を返している。もし、持っていた場合はname[false()]
と言う事になるので空のノード集合と、name[lang('ja')]
の和集合となる。持っていない場合は、逆にname[lang('ja')]
が空になり、左の式はname[true()]
になるので全てのname要素のposition() = 1
と、空のノード集合の和集合になると言う寸法である(よく考えたものだなと感心するが、ここまでくると一種のパズルだよ...)。