仅当变量与XSLT中的上一次迭代不同时才发出一些HTML

时间:2010-10-26 09:17:09

标签: xslt

使用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 >

4 个答案:

答案 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>

请注意

  1. 即使在<constraint>元素不是兄弟的情况下,或者即使其中一些元素是彼此的祖先/后代,转换也会起作用。

  2. 在这种情况下,有必要收集变量中的所有<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