题
给定以下 xml:
<container>
<val>2</val>
<id>1</id>
</container>
<container>
<val>2</val>
<id>2</id>
</container>
<container>
<val>2</val>
<id>3</id>
</container>
<container>
<val>4</val>
<id>1</id>
</container>
<container>
<val>4</val>
<id>2</id>
</container>
<container>
<val>4</val>
<id>3</id>
</container>
我想返回类似的东西
2 - 1
2 - 3
4 - 1
4 - 3
使用节点集,我能够通过以下方式获取最后一次出现的情况:
exsl:node-set($list)/container[not(val = following::val)]
但我不知道如何获得第一个。
解决方案
获取每个“中的第一个和最后一个出现(文档顺序)<val>
“组,您可以使用 <xsl:key>
像这样:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="text" />
<xsl:key name="ContainerGroupByVal" match="container" use="val" />
<xsl:variable name="ContainerGroupFirstLast" select="//container[
generate-id() = generate-id(key('ContainerGroupByVal', val)[1])
or
generate-id() = generate-id(key('ContainerGroupByVal', val)[last()])
]" />
<xsl:template match="/">
<xsl:for-each select="$ContainerGroupFirstLast">
<xsl:value-of select="val" />
<xsl:text> - </xsl:text>
<xsl:value-of select="id" />
<xsl:value-of select="' '" /><!-- LF -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
编辑#1:稍微解释一下,因为这可能不会立即显而易见:
- 这
<xsl:key>
返回全部<container>
具有给定的节点<val>
. 。您使用key()
函数来查询它。 - 这
<xsl:variable>
就是这一切发生的地方。其内容如下:- 对于每个
<container>
文档中的节点(“//container
“) 查看… - …如果它具有相同的唯一 ID (
generate-id()
) 作为返回的第一个节点key()
或返回的最后一个节点key()
- 在哪里
key('ContainerGroupByVal', val)
返回一组<container>
匹配当前节点<val>
- 如果唯一 ID 匹配,则将该节点包含在选择中
- 对于每个
- 这
<xsl:for-each>
执行输出。它也可能是一个<xsl:apply-templates>
.
编辑#2:正如 Dimitre Novaatchev 在评论中正确指出的那样,您应该谨慎使用“//
" XPath 简写。如果您可以避免它,请务必这样做 - 部分原因是它可能会选择您不想要的节点,主要是因为它比更具体的 XPath 表达式慢。例如,如果您的文档如下所示:
<containers>
<container><!-- ... --></container>
<container><!-- ... --></container>
<container><!-- ... --></container>
</containers>
那么你应该使用“/containers/container
“ 或者 ”/*/container
“ 代替 ”//container
".
编辑#3:上述的另一种语法是:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="text" />
<xsl:key name="ContainerGroupByVal" match="container" use="val" />
<xsl:variable name="ContainerGroupFirstLast" select="//container[
count(
.
| key('ContainerGroupByVal', val)[1]
| key('ContainerGroupByVal', val)[last()]
) = 2
]" />
<xsl:template match="/">
<xsl:for-each select="$ContainerGroupFirstLast">
<xsl:value-of select="val" />
<xsl:text> - </xsl:text>
<xsl:value-of select="id" />
<xsl:value-of select="' '" /><!-- LF -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
解释:XPath 联合运算符“|
" 将其参数组合成一个节点集。根据定义,节点集不能包含重复的节点 - 例如:”. | . | .
" 将创建一个仅包含一个节点(当前节点)的节点集。
这意味着,如果我们从当前节点(“.”)创建联合节点集,则“key(…)[1]
“节点和”key(…)[last()]
“节点,当(且仅当)当前节点等于其他两个节点之一时,它的节点计数将为 2,在所有其他情况下,计数将为 3。
其他提示
<强> 予。 XSLT 1.0
基本上与Tomalak的解决方案相同,但更容易理解 它也是完整的,因此您只需要复制并粘贴XML文档和转换,然后只需按<!> QUOT;变换QUOT <!>;您最喜欢的XSLT IDE的按钮:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kContByVal" match="container"
use="val"/>
<xsl:template match="/*">
<xsl:for-each select=
"container[generate-id()
=
generate-id(key('kContByVal',val)[1])
]
">
<xsl:variable name="vthisvalGroup"
select="key('kContByVal', val)"/>
<xsl:value-of select=
"concat($vthisvalGroup[1]/val,
'-',
$vthisvalGroup[1]/id,
'
'
)
"/>
<xsl:value-of select=
"concat($vthisvalGroup[last()]/val,
'-',
$vthisvalGroup[last()]/id,
'
'
)
"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
将此转换应用于最初提供的XML文档(编辑为格式良好)时:
<t>
<container>
<val>2</val>
<id>1</id>
</container>
<container>
<val>2</val>
<id>2</id>
</container>
<container>
<val>2</val>
<id>3</id>
</container>
<container>
<val>4</val>
<id>1</id>
</container>
<container>
<val>4</val>
<id>2</id>
</container>
<container>
<val>4</val>
<id>3</id>
</container>
</t>
生成了想要的结果:
2-1
2-3
4-1
4-3
请注意:
- 我们使用Muenchian方法进行分组,为每组具有
container
相同值的元素找到一个val
元素。 - 从具有相同
<xsl:for-each-group>
值的current-group()
元素的整个节点列表中,我们输出组中第一个<=>元素和最后一个元素所需的数据<= >小组中的元素。
醇>
-
使用<=> XSLT指令。
-
使用<=>功能。
醇>
<强> II。 XSLT 2.0
此转化:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:for-each-group select="container"
group-by="val">
<xsl:for-each select="current-group()[1], current-group()[last()]">
<xsl:value-of select=
"concat(val, '-', id, '
')"/>
</xsl:for-each>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
应用于上述相同的XML文档时,产生想要的结果:
<*>请注意: