调用具有不同上下文的 xsl 模板

Calling a xsl template with a different context

我正在修改一个 XSL,它已经带有一些模板输出与当前上下文节点相关的数据。我想用不同的上下文调用相同的模板,这样我就不必通过传递额外的参数来更改现有模板。

例如XML:

<anyRoot>
 <level1>
     <a>xxxxxx</a>
     <b>yyyyyy</b>
     <level2>
         <a>aaaaa</a>
         <b>bbbbbb</b>
         <c>cccccc</c>
         <d>dddddd</d>
     </level2>
 </level1>
 <level1>
     <a>zzzzzz</a>
     <b>jjjjjj</b>
     <level2>
         <a>nnnnn</a>
         <b>bbbbbb</b>
         <c>cccccc</c>
         <d>dddddd</d>
     </level2>
 </level1>
</anyRoot>

理论上的 XSL。请注意 "context=" 属性无效,但我把它放在那里是为了解释我的想法:

...
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:call-template name="testTmplate"/>
   <xsl:call-template name="testTmplate" context=".."/> <!-- passing parent of level2-->
</xsl:for-each>
...

<xsl:template name="testTmplate">
    <xsl:value-of select="./a"/>
</xsl:template>

这是我希望看到的输出:

aaaaa
xxxxxxx

nnnnnnn
zzzzzzz

如果你想改变上下文,你真的应该在这里使用 xsl:apply-templates 和匹配的模板。

例如

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

<xsl:template match="/">
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:apply-templates select="a" />
   <xsl:apply-templates select="../a" />
</xsl:for-each>
</xsl:template>

<xsl:template match="a">
    <xsl:value-of select="."/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

但是,如果您的实际 XSLT 中有另一个模板也匹配 "a" 元素,您可以使用 mode 属性区分您需要的模板,如下所示:

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

<xsl:template match="/">
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:apply-templates select="a" mode="testTmplate" />
   <xsl:apply-templates select="../a" mode="testTmplate" />
</xsl:for-each>
</xsl:template>

<xsl:template match="a" mode="testTmplate">
    <xsl:value-of select="."/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

编辑:如果你真的想调用一个现有的名称模板这个方法,只需从匹配的模板中调用它即可。试试这个...

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

<xsl:template match="/">
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:call-template name="testTmplate"/>
   <xsl:apply-templates select=".." mode="testTmplate" />
</xsl:for-each>
</xsl:template>

<xsl:template match="*" mode="testTmplate">
    <xsl:call-template name="testTmplate"/>
</xsl:template>

<xsl:template name="testTmplate">
    <xsl:value-of select="a"/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

原来的 ./a 中的 ./ 是多余的,正如@tim-c 所说,使用 xsl:apply-templates 可能会更好。但是,如果您不想过多地干扰现有的 xsl:call-template 和命名模板设置,您可以添加一个默认为上下文节点的参数(例如,$context),然后将其覆盖为必要:

<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:call-template name="testTmplate"/>
   <xsl:call-template name="testTmplate">
       <xsl:param name="context" select=".."/> <!-- passing parent of level2-->
   </xsl:call-template>
</xsl:for-each>
...

<xsl:template name="testTmplate">
    <xsl:param name="context" select="."/>
    <xsl:value-of select="$context/a"/>
</xsl:template>

因此 ./a 变得比 $context/a 更有用。

下面走错了!请参阅 Tim 的回答以了解正确的方法。但是,假设您已经命名了您不能或不会更改的模板,并且您想重用它们,请参见下文。当然,根据您当前的代码,您可以将参数添加到模板中,或者最好仍然使用带模式的未命名模板。

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

  <xsl:template match="/">
    <xsl:for-each select="/anyRoot/level1/level2">
      <xsl:call-template name="testTmplate"/>
    </xsl:for-each>
    <xsl:for-each select="/anyRoot/level1">
      <xsl:call-template name="testTmplate"/>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="testTmplate">
    <xsl:value-of select="./a"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

一种不更改现有命名模板的方法是使用 xsl:for-each:

更改上下文
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:call-template name="testTmplate"/>
   <xsl:for-each select=".."> <!-- parent of level2 -->
       <xsl:call-template name="testTmplate"/>
   </xsl:for-each>
</xsl:for-each>

我对推荐这个犹豫不决,因为在我看来,它的可读性不佳,但是在许多模板中乱扔 $context 可能最终也不那么可读。

谢谢蒂姆。您的答案游戏是一个非常重要的线索,所以我最终为第二个模板调用添加了一个新模板(从父级提取数据的模板)。这个新模板充当包装器模板来调用现有模板。请参阅下面的答案。

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

    <xsl:template match="/">
        <xsl:for-each select="/anyRoot/level1/level2">
            <xsl:call-template name="testTemplate" />
            <xsl:apply-templates select=".." mode="testTemplateWrapper" />
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="testTemplate">
        <xsl:value-of select="a"/>
        <xsl:text>&#10;</xsl:text>
    </xsl:template>

    <xsl:template match="level1" mode="testTemplateWrapper">
        <xsl:call-template name="testTemplate" />
    </xsl:template>

</xsl:stylesheet>