调用具有不同上下文的 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> </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> </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> </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> </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> </xsl:text>
</xsl:template>
<xsl:template match="level1" mode="testTemplateWrapper">
<xsl:call-template name="testTemplate" />
</xsl:template>
</xsl:stylesheet>
我正在修改一个 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> </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> </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> </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> </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> </xsl:text>
</xsl:template>
<xsl:template match="level1" mode="testTemplateWrapper">
<xsl:call-template name="testTemplate" />
</xsl:template>
</xsl:stylesheet>