xsltapply-templates

apply-templates ambiguous match warning


I get a warning

ambiguous rule match

from the processor for the templates copyReference and the identity transform.

<xsl:template name="processChildNodes">
    <xsl:param name="El"/>

    <xsl:for-each select="$El/node()">
        <xsl:choose>
            <xsl:when test="@sameas">
                <xsl:apply-templates mode="copyReference" select="id(substring-after(@sameas, '#'))"/>
            </xsl:when>         
            <xsl:otherwise>
                <xsl:copy-of select="." />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
</xsl:template>


<xsl:template match="*" mode="copyReference" name="copyReference">
    <xsl:copy>
        <xsl:apply-templates select="@* except (@stem.dir, @stem.sameas)"/>
    </xsl:copy>
</xsl:template>



<xsl:template match="node() | @*" mode="#all">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>

Here is an xml snippet:

<layer>         
        <note oct="3" pname="b" stem.dir="up" stem.sameas="#note_17544b" xml:id="note_17544"/>          
</layer>
<layer>
    <note oct="4" pname="d" xml:id="note_17592"/>
    <note sameas="#note_17544" xml:id="note_17544b"/>   
</layer>

What I want to do is just to copy the node which is referenced from the @sameas-attribute without @stem.dir and @stem.sameas. There could be different nodes local-names() on which the will be applied on. So I'd rather not specify the node names in the @match-attribute of the copyReference template. I thought if I pass the nodes I need with @select-attribute and also add @mode it will match only what I need. And actually it works, but as I'm getting the warning something should be wrong.


Solution

  • node() is short-hand for *|text()|comment()|processing-instruction() and so because the identity template has mode="#all" on it, it will match any element with the same priority as the "copyReference" template when the "copyReference" mode is used.

    The solution depends on what else your stylesheet does, but there are a number of possibilities

    1. Remove mode="#all" from the identity template (this would only work if there were not other modes in your XSLT)
    2. Add priority="2" to your "copyReference" template, so that when the mode "copyReference" was used, your specific template would get priority.
    3. Change <xsl:apply-templates mode="copyReference"... to be an xsl:for-each instead and do away with the template match.
    4. Change the "copyReference" template to explicitly match "note" rather than "*" as this would then give it a higher priority (but this obviously assumes you would only ever need to match note elements)