首页 文章

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

提问于
浏览
2

我想在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 是唯一的

  • 如果有多个具有相同 fID 属性值的 elementA 节点,则仅选择具有最新 dateTime 的节点 .

所以在我的例子中我想选择第一个和第三个 elementA .

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

2 回答

  • 1

    Here is a pure, single and efficient (no sorting) XPath 2.0 expression ,选择想要的元素:

    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]
    

    Here is a proof ,其中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>
    

    When the above transformation is applied on this source XML document

    <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>
    

    the wanted, correct result is produced

    <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"/>
    

    Note on efficiency

    此XPath表达式仅使用 max() 函数,即

    O(N)

    • 优于使用排序的解决方案的O(N * log(N)) .
  • 2

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

相关问题