如何根据属性选择具有条件的节点

时间:2015-07-23 14:04:46

标签: xml xslt xpath xslt-2.0 xpath-2.0

我想在XSLT中使用XPath来选择具有基于属性值的条件的节点。

为了说明我的问题,我有一个简短的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>

我想在以下条件下选择所有elementA个节点:

  • 属性fID是唯一的
  • 如果有多个elementA个节点具有相同的fID属性值,则只会选择最新dateTime的节点。

因此,在我的示例中,我想选择第一个和第三个elementA

如何在XSLT 2.0中使用XPath 2.0实现这一目标?

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

我会在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