如何 select 具有基于属性的条件的节点
How to select nodes with conditions based on attributes
我想将 XSLT 中的 XPath 用于 select 具有基于属性值的条件的节点。
为了说明我的问题,我有一个简短的例子 XML 实例如下:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<elementA fID="2013_4_20150722_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T14:14:22+02:00"/>
</root>
我想 select 所有 elementA
节点满足以下条件:
- 属性
fID
是唯一的
- 如果有多个
elementA
节点具有相同的 fID
属性值,则只有具有最新 dateTime
的节点将被 selected。
所以在我的例子中我想select第一个和第三个elementA
。
如何在 XSLT 2.0 中使用 XPath 2.0 实现此目的?
我会在 XSLT 2.0 中进行分组和排序,如果您想在 XPath 中使用它们,您可以编写一个用户定义的函数来包装该功能:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:output indent="yes"/>
<xsl:function name="mf:group-and-sort" as="element(elementA)*">
<xsl:param name="input" as="element(elementA)*"/>
<xsl:for-each-group select="$input" group-by="@fID">
<xsl:variable name="sorted-group" as="element(elementA)*">
<xsl:perform-sort select="current-group()">
<xsl:sort select="xs:dateTime(@dateTime)" order="descending"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:sequence select="$sorted-group[1]"/>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="root">
<xsl:copy>
<xsl:variable name="max-elementAs" select="mf:group-and-sort(elementA)"/>
<xsl:copy-of select="$max-elementAs"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
这是一个纯粹、单一且高效(无排序)的 XPath 2.0 表达式,它选择了想要的元素:
for $fid in distinct-values(/*/*/@fID),
$maxtime in max(/*/*[@fID eq $fid]/@dateTime/xs:dateTime(.))
return
(/*/*[@fID eq $fid and xs:dateTime(@dateTime) eq $maxtime])[1]
这里有一个证明,其中使用 XSLT 只是将计算表达式的结果复制到输出:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:sequence select=
"for $fid in distinct-values(/*/*/@fID),
$maxtime in max(/*/*[@fID eq $fid]/@dateTime/xs:dateTime(.))
return
(/*/*[@fID eq $fid and xs:dateTime(@dateTime) eq $maxtime])[1]
"/>
</xsl:template>
</xsl:stylesheet>
当上述转换应用于此源时 XML 文档:
<root>
<elementA fID="2013_4_20150722_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T12:14:22+00:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T14:14:22+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T14:14:22+02:00"/>
</root>
产生了想要的、正确的结果:
<elementA fID="2013_4_20150722_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T12:14:22+00:00"/>
效率注意事项:
这个XPath表达式只使用了max()
函数,也就是
O(N)
-- 优于使用排序的解决方案的 O(N*log(N))。
我想将 XSLT 中的 XPath 用于 select 具有基于属性值的条件的节点。
为了说明我的问题,我有一个简短的例子 XML 实例如下:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<elementA fID="2013_4_20150722_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T14:14:22+02:00"/>
</root>
我想 select 所有 elementA
节点满足以下条件:
- 属性
fID
是唯一的 - 如果有多个
elementA
节点具有相同的fID
属性值,则只有具有最新dateTime
的节点将被 selected。
所以在我的例子中我想select第一个和第三个elementA
。
如何在 XSLT 2.0 中使用 XPath 2.0 实现此目的?
我会在 XSLT 2.0 中进行分组和排序,如果您想在 XPath 中使用它们,您可以编写一个用户定义的函数来包装该功能:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:output indent="yes"/>
<xsl:function name="mf:group-and-sort" as="element(elementA)*">
<xsl:param name="input" as="element(elementA)*"/>
<xsl:for-each-group select="$input" group-by="@fID">
<xsl:variable name="sorted-group" as="element(elementA)*">
<xsl:perform-sort select="current-group()">
<xsl:sort select="xs:dateTime(@dateTime)" order="descending"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:sequence select="$sorted-group[1]"/>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="root">
<xsl:copy>
<xsl:variable name="max-elementAs" select="mf:group-and-sort(elementA)"/>
<xsl:copy-of select="$max-elementAs"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
这是一个纯粹、单一且高效(无排序)的 XPath 2.0 表达式,它选择了想要的元素:
for $fid in distinct-values(/*/*/@fID),
$maxtime in max(/*/*[@fID eq $fid]/@dateTime/xs:dateTime(.))
return
(/*/*[@fID eq $fid and xs:dateTime(@dateTime) eq $maxtime])[1]
这里有一个证明,其中使用 XSLT 只是将计算表达式的结果复制到输出:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:sequence select=
"for $fid in distinct-values(/*/*/@fID),
$maxtime in max(/*/*[@fID eq $fid]/@dateTime/xs:dateTime(.))
return
(/*/*[@fID eq $fid and xs:dateTime(@dateTime) eq $maxtime])[1]
"/>
</xsl:template>
</xsl:stylesheet>
当上述转换应用于此源时 XML 文档:
<root>
<elementA fID="2013_4_20150722_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T12:14:22+00:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T14:14:22+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T14:14:22+02:00"/>
</root>
产生了想要的、正确的结果:
<elementA fID="2013_4_20150722_0" dateTime="2015-07-13T01:04:20+02:00"/>
<elementA fID="2013_4_20150721_0" dateTime="2015-07-20T12:14:22+00:00"/>
效率注意事项:
这个XPath表达式只使用了max()
函数,也就是
O(N)
-- 优于使用排序的解决方案的 O(N*log(N))。