XSLT 2.0で便利になった機能(22) Nodeをサーチする関数 (その1)

ノードをサーチする機能を持った関数について紹介します.
 
■ id()
id()は、xs:stringをパラメータとして、それをID値とする要素を返します.XSLT2.0ではこれに、ドキュメントノードを表すnode()がパラメータとして追加されました.復習になりますが、id()関数を有効とするためには、属性がIDとして定義されていることをXSLTプロセッサに知らせてやらねばなりません.つまりDTDが必要でした.例えば、
 
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE shipping-list [
<!ELEMENT shipping-list (shipping*) >
<!ELEMENT shipping (transaction+) >
<!ATTLIST shipping month CDATA #REQUIRED>
<!ELEMENT transaction EMPTY >
<!ATTLIST transaction tid   ID    #REQUIRED
                      name  CDATA #REQUIRED
                      count CDATA #REQUIRED>
]>
<shipping-list>
    <shipping month="9">
        <transaction tid="tr_001" name="melon"  count="20"/>
        <transaction tid="tr_002" name="apple"  count="40"/>
        <transaction tid="tr_003" name="orange" count="10"/>
        <transaction tid="tr_005" name="pear"   count="30"/>
    </shipping>
    <shipping month="10">
        <transaction tid="tr_011" name="grape"  count="20"/>
        <transaction tid="tr_012" name="apple"  count="40"/>
        <transaction tid="tr_013" name="orange" count="10"/>
        <transaction tid="tr_005" name="banana" count="30"/>
    </shipping>
</shipping-list>
というような入力XMLデータ(validではありません)に対して、
<xsl:template match="/">
    <xsl:message>id('tr_003')/@name="<xsl:value-of select="id('tr_003')/@name"/>"</xsl:message>
</xsl:template>
 
のようなテンプレートは、id('tr_003')/@name="orange" と表示します.またtidがユニークでない場合、id('tr_005')/@name="pear"を表示します.このような場合、最初のものが採用されるように「寛容」な仕様になっています.
 
XSLT2.0によって追加された、2番目のパラメータにより、document()またはdoc()で複数の入力がある場合に、どちらでも自由に選択できるようになりました.
余談ですがテンポラリツリーでこの機能を試してみました.
 
<xsl:variable name="tempTree" as="document-node()">
    <xsl:document>
        <shipping-list>
            <shipping month="9">
                <transaction xml:id="tr_001" name="melon"  count="20"/>
                <transaction xml:id="tr_002" name="apple"  count="40"/>
                <transaction xml:id="tr_003" name="orange" count="10"/>
                <transaction xml:id="tr_005" name="pear"   count="30"/>
            </shipping>
            <shipping month="10">
                <transaction xml:id="tr_011" name="grape"  count="20"/>
                <transaction xml:id="tr_012" name="apple"  count="40"/>
                <transaction xml:id="tr_013" name="orange" count="10"/>
                <transaction xml:id="tr_005" name="banana" count="30"/>
            </shipping>
        </shipping-list>
    </xsl:document>
</xsl:variable>
 
これに対して、
 
<xsl:template match="/">
    <xsl:message>id('tr_011',$tempTree)/@name="<xsl:value-of select="id('tr_011',$tempTree)/@name"/>"</xsl:message>
</xsl:template>
 
は id('tr_011',$tempTree)/@name="grape" と表示します.xml:idという属性は、DTDがなくともそれがIDであることをXSLTプロセッサに知らせてくれます.
 
■ idref()
idref()は、xs:stringをパラメータとして、それをIDREF, IDREFS値とするノードを返します.XSLT1.0ではこの関数はありませんでした.XSLT2.0では、id()と同様にドキュメントノードを表すnode()がパラメータとしてあります.
例えば家族を表現する次のようなXMLデータがあるとします
 
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE family [
<!ELEMENT family (person*) >
<!ELEMENT person (name,(spouse|child|parent)*) >
<!ATTLIST person id     ID     #REQUIRED>
<!ELEMENT name   (#PCDATA)>
<!ELEMENT spouse EMPTY>
<!ATTLIST spouse ref    IDREF  #REQUIRED>
<!ELEMENT child  EMPTY>
<!ATTLIST child  ref    IDREFS #REQUIRED>
<!ELEMENT parent EMPTY>
<!ATTLIST parent ref    IDREFS #REQUIRED>
]>
<family>
    <person id="p_001">
        <name>Akio Ito</name>
        <spouse ref="p_002"/>
        <child ref="p_004"/>
    </person>
    <person id="p_002">
        <name>Mie Ito</name>
        <spouse ref="p_001"/>
        <child ref="p_004"/>
    </person>
    <person id="p_003">
        <name>Yukie Ito</name>
        <spouse ref="p_004"/>
        <child ref="p_005 p_006 p_007"/>
    </person>
    <person id="p_004">
        <name>Toshikazu Ito</name>
        <parent ref="p_001 p_002"/>
        <spouse ref="p_003"/>
        <child ref="p_005 p_006 p_007"/>
    </person>
    <person id="p_005">
        <name>Kouichiro Ito</name>
        <parent ref="p_003 p_004"/>
    </person>
    <person id="p_006">
        <name>Tomomi Ito</name>
        <parent ref="p_003 p_004"/>
    </person>
    <person id="p_007">
        <name>Natumi Ito</name>
        <parent ref="p_003 p_004"/>
    </person>
</family>
 
これに対して
 
    <xsl:variable name="idref" select="idref('p_003')"/>
 
は、"p_003"をIDREF, IDREFSとして持つ属性ノード(この場合は@ref)を返します.ですから、
 
<xsl:template match="/">
    <xsl:variable name="idref" select="idref('p_003')"/>
    <xsl:for-each select="$idref">
        <xsl:message>no=<xsl:value-of select="position()"/> name="<xsl:value-of select="parent::*/parent::person/name"/>"</xsl:message>
    </xsl:for-each>
</xsl:template>
 
は、次を出力します.
 
no=1 name="Toshikazu Ito"
no=2 name="Kouichiro Ito"
no=3 name="Tomomi Ito"
no=4 name="Natumi Ito"
 
(続く)