通过XSL键遍历/循环:如何?

时间:2010-05-03 12:11:34

标签: xslt loops foreach key

有没有办法遍历一个键并输出它包含的所有值?

   <xsl:key name="kElement" match="Element/Element[@idref]" use="@idref" />

我虽然这样:

<xsl:for-each select="key('kElement', '.')">
  <li><xsl:value-of select="." /></li>
</xsl:for-each>

然而,这不起作用。我只想列出一个键中的所有值以进行测试。

问题很简单:如何做到这一点?

4 个答案:

答案 0 :(得分:13)

你做不到。这不是关键所针对的。

当且仅当每个元素的键相同时,您才可以使用对key()的单个调用遍历键中的每个元素。

如果您需要循环定义键的所有内容,则可以使用match="..."元素的<key>属性中的表达式。

所以,如果您有这样的文件:

<root>
  <element name="Bill"/>
  <element name="Francis"/>
  <element name="Louis"/>
  <element name="Zoey"/>
</root>

这样的键定义如下:

<xsl:key name="survivors" match="element" use="@name"/>

您可以使用其match属性的内容遍历密钥使用的内容:

<xsl:for-each select="element">
  <!-- stuff -->
</xsl:for-each>

或者,如果每个元素都有一些共同点:

<root>
  <element name="Bill" class="survivor"/>
  <element name="Francis" class="survivor"/>
  <element name="Louis" class="survivor"/>
  <element name="Zoey" class="survivor"/>
</root>

然后你可以像这样定义你的密钥:

<xsl:key name="survivors" match="element" use="@class"/>

迭代所有这样的元素:

<xsl:for-each select="key('survivors', 'survivor')">
  <!-- stuff -->
</xsl:for-each>

因为每个元素共享class属性的值“幸存者”。

在您的情况下,您的密钥是

<xsl:key name="kElement" match="Element/Element[@idref]" use="@idref" />

所以你可以遍历它所拥有的一切:

<xsl:for-each select="Element/Element[@idref]">
  <!-- stuff -->
</xsl:for-each>

答案 1 :(得分:5)

CAN 创建用于循环的密钥 - 如果您只是在密钥元素的use属性中指定常量

<xsl:key name="survivors" match="element" use="'all'"/>

然后您可以通过以下方式遍历所有元素:

<xsl:for-each select="key('survivors','all')">
    ...
</xsl:for-each>

或计算他们:

<xsl:value-of select="count(key('survivors','all'))"/>

请注意,常量可以是任何字符串,甚至可以是数字 - 但“全部”读数都很好。

但是,您无法使用此密钥来查找有关各个条目的信息(因为它们都具有相同的密钥)。

换句话说,有两种可能的键:

  1. “查找键”=使用属性
  2. 中具有不同索引的标准键
  3. “循环键”=使用属性中带常量的键
  4. 我不知道这个方法的执行效率如何,但它确实通过避免在整个XSL代码中重复相同(可能非常复杂)的XPath表达式来提高XSL的维护效率。

答案 2 :(得分:2)

不要在编程语言术语中考虑XSL密钥,而应将它们视为SQL的记录集。这样可以更好地理解。对于作为

创建的给定密钥索引
<xsl:key name="paths" match="path" use="keygenerator()">

它可以是“迭代”/“走完”,如下所示

<xsl:for-each select="//path[generate-id()=generate-id(key('paths',keygenerator())[1])]">

要理解这个神奇的数字[1],让我们看看下面的例子:

考虑这个XML片段

<root>
    <Person>
        <name>Johny</name>
        <date>Jan10</date>
        <cost itemID="1">34</cost>
        <cost itemID="1">35</cost>
        <cost itemID="2">12</cost>
        <cost itemID="3">09</cost>
    </Person>
    <Person>
        <name>Johny</name>
        <date>Jan09</date>
        <cost itemID="1">21</cost>
        <cost itemID="1">41</cost>
        <cost itemID="2">11</cost>
        <cost itemID="2">14</cost>
    </Person>
</root>

使用此XSL进行转换。

  <xsl:for-each select="*/Person">
  <personrecords>
       <xsl:value-of select="generate-id(.)" />--
       <xsl:value-of select="name"/>--
       <xsl:value-of select="date"/>--      
  </personrecords>
  </xsl:for-each>

  <xsl:for-each select="*/*/cost">
  <costrecords>
      <xsl:value-of select="generate-id(.)" />--
      <xsl:value-of select="../name"/>-- 
      <xsl:value-of select="../date"/>-- 
      <xsl:value-of select="@itemID"/>-- 
      <xsl:value-of select="text()"/>
  </costrecords>
  </xsl:for-each>

上面的XSL转换以Person的形式列出了cost节点和idpxxxxxxx节点的唯一ID,如下所示。

  1. <personrecords>idp2661952--Johny--Jan10--      </personrecords>
  2. <personrecords>idp4012736--Johny--Jan09--      </personrecords>

  3. <costrecords>idp2805696--Johny-- Jan10-- 1-- 34</costrecords>
  4. <costrecords>idp4013568--Johny-- Jan10-- 1-- 35</costrecords>
  5. <costrecords>idp2808192--Johny-- Jan10-- 2-- 12</costrecords>
  6. <costrecords>idp2808640--Johny-- Jan10-- 3-- 09</costrecords>
  7. <costrecords>idp2609728--Johny-- Jan09-- 1-- 21</costrecords>
  8. <costrecords>idp4011648--Johny-- Jan09-- 1-- 41</costrecords>
  9. <costrecords>idp2612224--Johny-- Jan09-- 2-- 11</costrecords>
  10.<costrecords>idp2610432--Johny-- Jan09-- 2-- 14</costrecords>

让我们使用costname值组合在itemID条记录上创建密钥
  

 <xsl:key name="keyByNameItem" match="cost" use="concat(../name, '+', @itemID)"/>

手动查看XML,上面的唯一键的数量将是三个: Johny + 1 Johny + 2 Johny + 3 < /强>

现在让我们使用下面的代码段来测试这个密钥。

   <xsl:for-each select="*/*/cost">
   <costkeygroup>
      <xsl:value-of select="generate-id(.)" />-- 
      (1)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[1] ) " />-- 
      (2)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[2] ) " />-- 
      (3)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[3] ) " />-- 
      (4)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', @itemID) )[4] ) " />
    </costkeygroup>
    </xsl:for-each>

结果如下:

  1. <costkeygroup>idp2805696-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  2. <costkeygroup>idp4013568-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  3. <costkeygroup>idp2808192-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
  4. <costkeygroup>idp2808640-- (1)idp2808640-- (2)-- (3)-- (4)</costkeygroup>
  5. <costkeygroup>idp2609728-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  6. <costkeygroup>idp4011648-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
  7. <costkeygroup>idp2612224-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
  8. <costkeygroup>idp2610432-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>

我们的兴趣在于努力了解[1][2][3][4]的重要性。在我们的例子中,密钥生成器是concat(../name, '+', @itemID)

对于给定的密钥,[1]指的是满足密钥生成器的节点的第一次出现。类似地,[2]指的是满足密钥生成器的节点的第二次出现。因此[2][3][4]等都是满足相同密钥的节点,因此可以认为是给定密钥的重复。重复数量取决于输入XML。因此:

  
    

Johny + 1 满足 4 节点(1)idp2805696--(2)idp4013568--(3)idp2609728--(4)idp4011648 < / em>的
    键 Johny + 2 满足 3 节点(1)idp2808192--(2)idp2612224--(3)idp2610432--(4) <登记/>     键 Johny + 3 满足 1 节点(1)idp2808640--(2) - (3) - (4)

  

因此,我们看到可以通过密钥访问XML的所有8个cost节点

这是一个结合转换结果的图像,有助于更好地理解。

enter image description here

红色方块表示 Johny + 1 的匹配节点。绿色方块表示 Johny + 3 的匹配节点。将idpxxxxxxx中的<costkeygroup>值与<costrecords>中的值相匹配。 <costrecords>帮助将idpxxxxxxx值映射到源XML。

外卖是,

  
    
      

XSL密钥不会过滤或消除节点。可以通过密钥访问包括重复的所有节点。因此,当我们说“遍历”密钥时,没有概念表示原始节点集合中的结果子集可用于处理密钥。

    
  

要在上面的示例中仅“遍历”键的唯一节点,请使用

 <xsl:for-each select="*/*/workTime[generate-id()=generate-id(key('keyByNameItem', concat(../name, '+', @itemID) )[1] ) ] ">

[1]表示给定键值的第一条记录表示为唯一记录。几乎总是使用[1]因为至少存在一个满足给定键值的节点。如果我们确定至少有2条记录可以满足密钥中的每个密钥值,我们可以继续使用[2]将记录集中的第二条记录标识为唯一记录。

P.S节点/记录/元素这两个词可以互换使用。

答案 3 :(得分:0)

虽然我们可以输出它包含的所有值,但无法遍历键。在XSLT2中,它比在XSLT1中更容易(例如,根据前面的答案使用fn:generate-id)。

使用fn:distinct-values

<xsl:variable name="e" select="."/>
<xsl:for-each select="distinct-values(Element/Element[@idref]/@idref)">
  <li key="{.}"><xsl:value-of select="key('kElement', ., $e )" /></li>
</xsl:for-each>

使用xsl:for-each-group

<xsl:for-each-group select="Element/Element[@idref]" group-by="@idref">
  <li key="{current-grouping-key()}"><xsl:value-of select="current-group()" /></li>
</xsl:for-each-group>