【问题标题】:Walk/loop through an XSL key: how?遍历/循环通过 XSL 键:如何?
【发布时间】:2010-05-03 12:11:34
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: xslt loops foreach key


    【解决方案1】:

    你不能。这不是钥匙的用途。

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

    如果您需要遍历定义键的所有内容,可以使用&lt;key&gt; 元素的match="..." 属性中的表达式。

    如果你有这样的文件:

    <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 属性的值“survivor”。

    在你的情况下,你的钥匙是

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

    所以你可以像这样遍历它的所有内容:

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

    【讨论】:

    • (+1) 有道理。这回答了我的问题。
    • 喜欢 L4D 参考,这个答案也帮助了我!
    【解决方案2】:

    可以创建一个用于循环的键 - 如果您只是在 key 元素的 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'))"/>
    

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

    但是,您不能使用此键查找有关各个条目的信息(因为它们都具有相同的键)。

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

    1. “查找键” = 在使用属性中具有不同索引的标准键
    2. “循环键” = 在使用属性中具有常量的键

    我不知道这种方法的执行效率如何,但是通过避免在整个 XSL 代码中重复相同(可能非常复杂)的 XPath 表达式,它确实使 XSL 的维护更加高效。

    【讨论】:

    • 很高兴听到它很有用! :-)
    【解决方案3】:

    与其将 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 sn-p

    <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 转换以 idpxxxxxxx 的形式列出了 Person 节点和 cost 节点的唯一 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>
    

    让我们使用nameitemID 值的组合在cost 记录上创建一个

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

    手动查看 XML,上面的唯一键数为 3:Johny+1Johny+2Johny+3。

    现在让我们使用下面的 sn-p 测试这个密钥。

       <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。因此:

    Key Johny+1 满足 4 个节点 (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648
    关键 Johny+2 满足 3 个节点 (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)Johny+3 满足 1 节点 (1)idp2808640-- (2)-- (3)-- (4)

    因此我们看到所有8个cost节点都可以通过key来访问XML。

    这是一张结合转换结果的图像,以帮助更好地理解。

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

    结论是,

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

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

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

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

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

    【讨论】:

      【解决方案4】:

      没有办法遍历键,尽管我们可以输出它包含的所有值。在 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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多