如何 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 节点满足以下条件:

所以在我的例子中我想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>

在线示例位于http://xsltransform.net/jyH9rNb

这是一个纯粹、单一且高效(无排序)的 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))。