Understanding `apply-templates` matching

2019-06-22 08:21发布

Am I correct in thinking that an apply-templates declaration should match all templates which could possibly be applied for a selection?

For example, given the following xml fragment:

<doc>
  <foo bar="1" baz="2">boz</foo>
</doc>

and the following stylesheet:

<xsl:template match="/">
  <xsl:apply-templates select="foo" mode="xyz" />
</xsl:template>

<xsl:template mode="xyz" match="foo[bar='1']">
  abc
</xsl:template>

<xsl:template mode="xyz" match="foo[baz='2']">
  def
</xsl:template>

I would expect the output to be:

abc
def

Is this correct?

标签: xslt
3条回答
放我归山
2楼-- · 2019-06-22 08:36

If you did want your templates to match for both attributes, then you would just need to adjust the match XPATH to select the attributes and put the relationship to foo in the predicate; rather than having two conflicting templates matching on the foo element with the same specificity(which have the same priority).

<?xml version="1.0" encoding="UTF-8"?>
  <xsl:stylesheet
   version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" encoding="UTF-8"/>

<xsl:template match="/">
  <xsl:apply-templates select="doc/foo"  />
</xsl:template>

<!--When templates match on foo, apply templates for it's attributes -->
<xsl:template match="foo">
    <xsl:apply-templates select="@*"/>
</xsl:template>

<!--Unique template match for the bar attribute -->
<xsl:template match="@bar[parent::foo and .='1']">
  abc
</xsl:template>

<!--Unique template match for the baz attribute -->
<xsl:template match="@baz[parent::foo and .='2']">
  def
</xsl:template>

</xsl:stylesheet>
查看更多
beautiful°
3楼-- · 2019-06-22 08:43

If you fix that XSLT code (you have some filter problems) and ran it, you should see:

def

Why? <xsl:apply-templates /> will match first template which satisfies your match condition. If you have two templates, you should to differ than by using that <xsl:apply-templates> mode attribute, or by using <xsl:template> priority attribute:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/doc">
        <xsl:apply-templates select="foo" mode="2" />
    </xsl:template>

    <xsl:template mode="1" match="foo[@bar='1']">
        abc
    </xsl:template>

    <xsl:template mode="2" match="foo[@baz='2']">
        def
    </xsl:template>

</xsl:stylesheet>
查看更多
Fickle 薄情
4楼-- · 2019-06-22 08:54

No, you don't get both outputs, as only one template will be selected. See this page for the rules on conflict resolution if there are multiple possible templates.

After fixing your stylesheet (similarly to how Rubens did it, but with same modes), this usually will result in the last template in your xslt file to be applied, so the output will be def. This is because the two templates have the same priority and if your xslt processor does not stop with an error the standard requires it to apply the last one:

It is an error if this 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.

查看更多
登录 后发表回答