使用XSLT我试图找出一种方法,当一个值与for-each循环的最后一次迭代不同时,只发出一个HTML表行。基本上我想让表看起来有分组的标题,只需在标题改变后写出标题。
我在概念上遇到一些麻烦,考虑到一旦变量被定义后你就无法改变变量值,我会如何做到这一点。
<xsl:for-each select="//databaseConstraint/constraint">
<xsl:variable name="caption" select="@caption" />
<tr>
<th colspan="2"><xsl:value-of select="$caption" /></th>
</tr>
...
</xsl:for-each >
更新我在下面尝试了,但是我收到了一个错误: NodeTest 在这里预计
<xsl:for-each select="//databaseConstraint/constraint">
<xsl:variable name="caption" select="@caption" />
<xsl:if test="not(@caption = preceding-sibling::@caption)">
<tr>
<th colspan="2">
<xsl:value-of select="$caption" />
</th>
</tr>
</xsl:if>
...
</xsl:for-each >
答案 0 :(得分:1)
<xsl:if test="not(@caption = preceding-sibling::@caption)">
这将测试标题是否等于作为上下文节点的兄弟的标题属性,即您正在处理的约束元素的兄弟。但我怀疑语法错误会“失败”,因为标题步骤有两个轴:preceding-sibling::
和attribute::
(@缩写)。
你可能想要的是
<xsl:if test="not(@caption = preceding-sibling::constraint[1]/@caption)">
这将做你想要的,它比Muenchian更简单,如果浏览器的XPath实现得体,它可能足够快,因为它只需要测试另一个节点,而不是所有先前的约束节点。
如果此策略对您的目的不够快,例如如果您有大量数据,可以使用Muenchian分组,正如@Frédéric所说。
添加: [1]
是[position() = 1]
的缩写。这意味着在=
的右侧,我们在当前元素之前只有@caption约束 。如果我们省略[1]
,我们会将当前元素的@caption值与所有前兄弟约束元素的@captions进行比较。
重要的是要意识到XPath =
运算符在节点集上运行(当有机会时),而不仅仅是单个值或节点。所以A = B
,其中A和B是节点集,如果节点集A的任何成员等于节点集B的任何成员,则返回true。这是而非像SQL中的连接。这是一个强大的操作,但你必须知道它在做什么。
另一个细节......为什么[1]
会产生紧接在当前之前的约束元素,而不是文档中的第一个?因为position()
反映了当前轴的方向,在本例中为preceding-sibling
。正如XPath spec所说,
这是 只包含的轴 上下文节点或之前的节点 文档顺序中的上下文节点是 a 反转轴。因此,祖先, 祖先或自我,先于,和 previous-sibling 轴是相反的 轴;所有其他轴都是前轴。 ...
成员的接近位置 相对于轴的节点集是 定义为节点的位置 在文档中排序的节点集中 如果轴是前轴,则排序 并且以反向文档顺序排序 如果轴是反转轴。该 第一个位置是1。
HTH。
答案 1 :(得分:1)
我正试图找到一种方法 值为时,发出HTML表行 与上一次迭代不同 for-each循环
这回答了字面上的问题。如果您想执行分组了解Muenchian grouping。
此转化:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vConstraints"
select="//databaseConstraint/constraint"/>
<table>
<xsl:for-each select="$vConstraints">
<xsl:variable name="vCaption" select="@caption" />
<xsl:variable name="vPos" select="position()"/>
<xsl:if test="not($vCaption = $vConstraints[$vPos -1]/@caption)">
<tr>
<th colspan="2"><xsl:value-of select="$vCaption" /></th>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
应用于以下XML文档:
<t>
<a>
<databaseConstraint>
<constraint caption="Simple Constraint"/>
</databaseConstraint>
<databaseConstraint>
<constraint caption="Simple Constraint"/>
</databaseConstraint>
</a>
<b>
<databaseConstraint>
<constraint caption="Complex Constraint"/>
</databaseConstraint>
</b>
</t>
生成想要的正确结果:
<table>
<tr>
<th colspan="2">Simple Constraint</th>
</tr>
<tr>
<th colspan="2">Complex Constraint</th>
</tr>
</table>
请注意:
即使在<constraint>
元素不是兄弟的情况下,或者即使其中一些元素是彼此的祖先/后代,转换也会起作用。
在这种情况下,有必要收集变量中的所有<constraint>
元素并重新计算当前position()
元素的<constraint>
,以便可以将它与节点集中上一个位置的<constraint>
元素进行比较。
答案 2 :(得分:0)
使用XSLT 1.0并不容易。如果您的实现支持XSLT 2.0功能,请使用xsl:for-each-group。如果您遇到1.0,请查看this Muenchian implementation using keys。
答案 3 :(得分:0)
您可能尝试使用previous-previous-sibling将当前节点(或其中一个属性)与前一个节点进行比较,并仅在current!= preceding之前显示该表。请参阅XPath Axes。