与其将 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>
让我们使用name 和itemID 值的组合在cost 记录上创建一个键。
<xsl:key name="keyByNameItem" match="cost" use="concat(../name, '+', @itemID)"/>
手动查看 XML,上面的唯一键数为 3:Johny+1、Johny+2 和 Johny+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 的匹配节点。将<costkeygroup> 中的idpxxxxxxx 值与<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 单词节点/记录/元素可以互换使用。