MrD at KookerellaLtd
MrD at KookerellaLtd

Reputation: 2797

XSLT style - pattern matching multiple templates

This is a question about xslt 1.0 (but i've included the general xslt tag as it may apply more widely).

lets say we want to take this xml

<root>
  <vegetable>
    <carrot colour="orange"/>      
    <leek colour="green"/>
  </vegetable>  
</root>

and transform it to cook the vegetables if they are root vegetables so this..

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="carrot">
    <xsl:copy>
      <xsl:attribute name="cooked">true</xsl:attribute>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="leek">    
  </xsl:template>
</xsl:stylesheet>

so the xslt recursively processes the data, and when it finds multiple matching templates e.g. leek and carrot, it takes the last one, effectively overriding.

Sometimes accepted answers in this site have this style, e.g. XSLT copy-of but change values

other answers specifically about multiple matching templates e.g. XSLT Multiple templates with same match

state

Having two templates of the same priority that match the same node is an error according to the XSLT specification

It is an error if [the algorithm in section 5.5] leaves more than one matching template rule. An XSLT processor may signal the error; if it does not signal the error, it must recover by choosing, from amongst the matching template rules that are left, the one that occurs last in the stylesheet.

so....we can avoid this by either using priority or by matching to explicitly excluding the the overlap, something like this.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="@* | node()[not(self::carrot) or not(self::leek)]">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

  <xsl:template match="carrot">
    <xsl:copy>
      <xsl:attribute name="cooked">true</xsl:attribute>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="leek">    
  </xsl:template>
</xsl:stylesheet>

I get the feeling that lots of devs actually simply use the default fallback behaviour and let the processor use the last match, this is similar in style to pattern matching in most functional languages where the 1st match is used.

I also personally am not a fan of priority, it feels a bit like magic numbers, where i have to scan and remember the priority of the pattern matches to work out whats going on.

The approach to explicitly exclude overlaps, seems sensible, but in practice requires complex logic and creates coupling between templates, if i extend/add a new match, i potentially have to amend constrain another.

Is the above a correct summary? Is there an accepted style (even if it contradicts the spec)?

Upvotes: 0

Views: 318

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 117073

I think you may be missing the fact that there is no error in the given example, therefore the rule of applying the template that occurs last in the stylesheet is not invoked. You can verify this by switching the order of the templates and observing that the result remains unchanged.

There is no error because the identity transform has a priority of -0.5 while the specific templates have a priority of 0.

Read the entire specification for conflict resolution:
https://www.w3.org/TR/1999/REC-xslt-19991116/#conflict

Upvotes: 2

Related Questions