ツリーのノードの巡回 (2)

Namspaceノードは、XSLTの使用するツリーモデルのXDM ()によれば次のように解説されています.

6.4 Namespace Nodes
Each Namespace Node represents the binding of a namespace URI to a namespace prefix or to the default namespace. 

つまりネームスペースURIからネームスペースプレフィックス(接頭辞)への対応関係を記述しているのがNamespaceノードです.

では実際にテンプレートでこれを出してみようとするとうまくゆきません.なぜかと言うと

<xsl:apply-templates select="namespace::node()"/>

と書いてもエラーになりませんが、受け手を

<xsl:template match="namespace::node()">

と書くとXSLT 2.0ではエラーになってしまうからです.そうです、match属性に書くパターンにはnamespace軸は書けないのです.

そこで唯一の手段は次のようなコードを書くことになります.

    <xsl:for-each select="namespace::node()">
        <node>
            <xsl:attribute name="node-type">namespace</xsl:attribute>
            <xsl:variable name="nsNode" select="."/>
            <xsl:attribute name="name"><xsl:value-of select="name()"/></xsl:attribute>
            <xsl:value-of select="string(.)"/>
        </node>
    </xsl:for-each>

こうした場合の出力は次のようになります.要素毎にnamespaceノードがあって、確かにネームスペースURIとネームスペースプレフィックスの関係を保持していてくれることがわかります."xml"という接頭辞はxml:spaceやxsl:langを書くときに使うのですね.

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <node node-type="document-node">
Document title
The quick brown fox jumps over the lazy dog
</node>
   <node node-type="element" name="doc">
      <node node-type="namespace" name="xml">http://www.w3.org/XML/1998/namespace</node>
Document title
The quick brown fox jumps over the lazy dog
</node>
   <node node-type="text">
</node>
   <node node-type="element" name="title">
      <node node-type="namespace" name="xml">http://www.w3.org/XML/1998/namespace</node>Document title</node>
   <node node-type="text">Document title</node>
   <node node-type="text">
</node>
   <node node-type="element" name="chapter">
      <node node-type="namespace" name="xml">http://www.w3.org/XML/1998/namespace</node>
      <node node-type="attribute" name="toc">yes</node>
The quick brown fox jumps over the lazy dog
</node>
   <node node-type="text">
</node>
   <node node-type="comment"> これはコメントです</node>
   <node node-type="text">
</node>
   <node node-type="processing-instruction" name="pi">これは処理命令です.</node>
   <node node-type="text">
</node>
   <node node-type="element"
         namespace-uri="urn:x-antennahouse-1"
         local-name="p">
      <node node-type="namespace" name="ah1">urn:x-antennahouse-1</node>
      <node node-type="namespace" name="ah2">urn:x-antennahouse-2</node>
      <node node-type="namespace" name="xml">http://www.w3.org/XML/1998/namespace</node>
      <node node-type="attribute"
            namespace-uri="urn:x-antennahouse-2"
            local-name="break-before">page</node>The quick brown fox jumps over the lazy dog</node>
   <node node-type="text">The quick brown fox jumps over the lazy dog</node>
   <node node-type="text">
</node>
   <node node-type="text">
</node>
</root>

最終的なスタイルシートは次のようになりました.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
 exclude-result-prefixes="xs"
>
    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <root>
            <xsl:call-template name="process-node"/>
        </root>
    </xsl:template>
    
    <xsl:template match="child::node()|attribute::node()" name="process-node">
        <node>
            <xsl:attribute name="node-type">
                <xsl:choose>
                    <xsl:when test="self::document-node()">document-node</xsl:when>
                    <xsl:when test="self::element()">element</xsl:when>
                    <xsl:when test="self::attribute()">attribute</xsl:when>
                    <xsl:when test="self::text()">text</xsl:when>
                    <xsl:when test="self::comment()">comment</xsl:when>
                    <xsl:when test="self::processing-instruction()">processing-instruction</xsl:when>
                    <xsl:otherwise/>
                </xsl:choose>
            </xsl:attribute>
            <xsl:choose>
                <xsl:when test="string(namespace-uri())">
                    <xsl:attribute name="namespace-uri" select="namespace-uri()"/>
                    <xsl:attribute name="local-name" select="local-name()"/>
                </xsl:when>
                <xsl:when test="string(name())">
                    <xsl:attribute name="name" select="name()"/>
                </xsl:when>
                <xsl:otherwise/>
            </xsl:choose>
            <xsl:for-each select="namespace::node()">
                <node>
                    <xsl:attribute name="node-type">namespace</xsl:attribute>
                    <xsl:variable name="nsNode" as="node()" select="."/>
                    <xsl:attribute name="name"><xsl:value-of select="name()"/></xsl:attribute>
                    <xsl:value-of select="string(.)"/>
                </node>
            </xsl:for-each>
            <xsl:apply-templates select="attribute::node()"/>
            <xsl:value-of select="string(.)"/>
        </node>
        <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

oXygenはSaxon 9.5のXSLT 3.0(XPath 3.0)の実装が使えちゃいます.XSLTスタイルシートのバージョンを3.0にちょっと変えれば、スタイルシートは次のようにできます.これでまさしく、「すべてはノード」ということを理解していただけるのではないでしょうか?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
 exclude-result-prefixes="xs"
>
    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <root>
            <xsl:call-template name="process-node"/>
        </root>
    </xsl:template>
    
    <xsl:template match="child::node()|attribute::node()|namespace::node()" name="process-node">
        <node>
            <xsl:attribute name="node-type">
                <xsl:choose>
                    <xsl:when test="self::document-node()">document-node</xsl:when>
                    <xsl:when test="self::element()">element</xsl:when>
                    <xsl:when test="self::namespace-node()">namespace</xsl:when>
                    <xsl:when test="self::attribute()">attribute</xsl:when>
                    <xsl:when test="self::text()">text</xsl:when>
                    <xsl:when test="self::comment()">comment</xsl:when>
                    <xsl:when test="self::processing-instruction()">processing-instruction</xsl:when>
                    <xsl:otherwise/>
                </xsl:choose>
            </xsl:attribute>
            <xsl:choose>
                <xsl:when test="string(namespace-uri())">
                    <xsl:attribute name="namespace-uri" select="namespace-uri()"/>
                    <xsl:attribute name="local-name" select="local-name()"/>
                </xsl:when>
                <xsl:when test="string(name())">
                    <xsl:attribute name="name" select="name()"/>
                </xsl:when>
                <xsl:otherwise/>
            </xsl:choose>
            <xsl:apply-templates select="namespace::node()"/>
            <xsl:apply-templates select="attribute::node()"/>
            <xsl:value-of select="string(.)"/>
        </node>
        <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

oXygenはSaxon9.5が使えるので、XSLT 3.0などいろいろ試してみるにはうってつけです.