我正在处理一个XML文件,我希望在那里保留节点数量,以便在编写新节点时将其用作ID。
目前我有一个名为'counter'的全局变量。我能够在模板中访问它,但我还没有找到在模板中操作它的方法。
以下是我的XSLT文件的精简版本:
<xsl:variable name="counter" select="1" as="xs:integer"/>
<xsl:template match="/">
<xsl:for-each select="section">
<xsl:call-template name="section"></xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="section">
<!-- Increment 'counter' here -->
<span class="title" id="title-{$counter}"><xsl:value-of select="title"/></span>
</xsl:template>
有任何建议如何从这里开始?
答案 0 :(得分:44)
其他人已经解释了变量是如何不可变的 - 在XSLT中没有赋值语句(与一般的纯函数式编程语言一样)。
我可以替代迄今为止提出的解决方案。它避免了参数传递(在XSLT中这是冗长和丑陋的 - 即使我承认这一点。)
在XPath中,您可以简单地计算当前元素之前的<section>
个元素的数量:
<xsl:template name="section">
<span class="title" id="title-{1 + count(preceding-sibling::section)}">
<xsl:value-of select="title"/>
</span>
</xsl:template>
(注意:空格代码格式化不会出现在结果中,因为只有空格的文本节点会自动从样式表中删除。所以不要觉得强迫将指令放在同一行上。)
这种方法的一大优势(与使用position()
相反)是它仅依赖于当前节点,而不依赖于当前节点列表。如果您以某种方式更改了处理(例如,<xsl:for-each>
不仅处理了部分而且处理了其他元素),那么position()
的值将不再必然对应于<section>
元素的位置在你的文件中。另一方面,如果您使用上述count()
,那么它将始终对应于每个<section>
元素的位置。这种方法减少了与代码其他部分的耦合,这通常是一件非常好的事情。
count()的替代方法是使用<xsl:number>
指令。它的默认行为会将所有同名的元素编号在同一级别,这恰好是您想要的:
<xsl:template name="section">
<xsl:variable name="count">
<xsl:number/>
</xsl:variable>
<span class="title" id="title-{$count}">
<xsl:value-of select="title"/>
</span>
</xsl:template>
这是详细程度的权衡(如果你仍然想要使用属性值模板花括号,则需要额外的变量声明),但只是稍微如此,因为它也大大简化了你的XPath表达式。
还有更多改进的空间。虽然我们已经删除了对当前节点列表的依赖,但我们仍然依赖于当前节点。这本身并不是一件坏事,但从模板中查看当前节点是什么并不是很明显。我们所知道的是模板名为“section
”;为了确定正在处理什么,我们必须在代码中寻找其他地方。但即使如此也不一定如此。
如果您觉得可以一起使用<xsl:for-each>
和<xsl:call-template>
(如您的示例所示),请退后一步,找出如何使用<xsl:apply-templates>
。
<xsl:template match="/doc">
<xsl:apply-templates select="section"/>
</xsl:template>
<xsl:template match="section">
<xsl:variable name="count">
<xsl:number/>
</xsl:variable>
<span class="title" id="title-{$count}">
<xsl:value-of select="title"/>
</span>
</xsl:template>
这种方法不仅简洁(<xsl:apply-templates/>
替换<xsl:for-each>
和<xsl:call-template/>
),而且还可以立即清楚当前节点是什么。您所要做的就是查看match
属性,并立即知道您正在处理<section>
元素,而<section>
元素就是您正在计算的内容。
有关模板规则(即具有<xsl:template>
属性的match
元素)如何工作的简明说明,请参阅"How XSLT Works"。
答案 1 :(得分:8)
无法更改XSLT变量。您将从模板到模板传递值。
如果您使用的是XSLT 2.0,则可以使用参数并使用隧道将变量传播到正确的模板。
您的模板将如下所示:
<xsl:template match="a">
<xsl:param name="count" select="0">
<xsl:apply-templates>
<xsl:with-param select="$count+1"/>
</xsl:apply-templates>
</xsl:template>
如果要创建ID,请查看使用generate-id()。
答案 2 :(得分:6)
XSLT中的变量是不可变的,因此您必须考虑到这个问题。您可以直接使用position()
:
<xsl:template match="/">
<xsl:for-each select="section">
<xsl:call-template name="section"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="section">
<span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>
或者采用更加模板化的方式:
<xsl:template match="/">
<xsl:apply-templates select="section"/>
</xsl:template>
<xsl:template match="section">
<span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>
答案 3 :(得分:2)
变量是本地范围的,只能在xslt中读取。
答案 4 :(得分:2)
根据您的XSLT处理器,您可以将脚本功能引入XLST。例如,Microsoft XML库支持包含javascript。有关示例,请参阅http://msdn.microsoft.com/en-us/library/aa970889(VS.85).aspx。如果您计划在公共客户端浏览器上部署/执行XSLT,这种策略显然不会起作用;它必须由特定的XSLT处理器完成。
答案 5 :(得分:1)
您可以使用position()函数执行您想要的操作。它看起来像这样。
<xsl:template match="/">
<xsl:for-each select="section">
<xsl:call-template name="section">
<xsl:with-param name="counter" select="{position()}"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="section">
<xsl:param name="counter"/>
<span class="title" id="title-{$counter}">
<xsl:value-of select="title"/>
</span>
</xsl:template>
答案 6 :(得分:0)
我自己没试过,但你可以尝试将参数传递给模板。 在你的第一个模板中,你可以在for-each语句中将参数设置为count()(或者current()可能?),然后将该值传递给“section”模板。
上有更多内容答案 7 :(得分:0)
使用<xsl:variable name="RowNum" select="count(./preceding-sibling::*)" />
和 $ RowNum 作为递增值。
例如:<xsl:template name="ME-homeTiles" match="Row[@Style='ME-homeTiles']" mode="itemstyle">
<xsl:variable name="RowNum" select="count(./preceding-sibling::*)" />
...<a href="{$SafeLinkUrl}" class="tile{$RowNum}"><img ....></a>
这将为值tile1,tile2,tile3等链接创建类...