【问题标题】:XPath to get nodes with a matching child-node and highest value for other child-nodeXPath 获取具有匹配子节点和其他子节点最高值的节点
【发布时间】:2017-09-22 16:31:22
【问题描述】:

我需要 XPath 1.0 中的帮助来过滤以下 XML,以便我只获得具有不同“id”和最高“有效性/日期”的条:

<foo name="fooName">
    <bar name="barName">
        <id>1111</id>
        <validity>
            <date>20170920</date>
        </validity>
    </bar>
    <bar name="barName">
        <id>1111</id>
        <validity>
            <date>20170922</date>
        </validity>
    </bar>
    <bar name="barName">
        <id>1111</id>
        <validity>
            <date>20170921</date>
        </validity>
    </bar>
    <bar name="barName">
        <id>2222</id>
        <validity>
            <date>20170921</date>
        </validity>
    </bar>
    <bar name="barName">
        <id>2222</id>
        <validity>
            <date>20170923</date>
        </validity>
    </bar>
</foo>

我尝试了很多选择和研究,但无法找出确切的解决方案。

过滤后的预期 XML 应如下所示:

<foo name="fooName">
    <bar name="barName">
        <id>1111</id>
        <validity>
            <date>20170922</date>
        </validity>
    </bar>
    <bar name="barName">
        <id>2222</id>
        <validity>
            <date>20170923</date>
        </validity>
    </bar>
</foo>

【问题讨论】:

  • 这基本上是一个分组问题 - 见:jenitennison.com/xslt/grouping/muenchian.html 在每个组内,按日期排序并输出第一条(或最后一条)记录。
  • @michael.hor257k 谢谢。我将探索更多关于 Muenchian 分组的内容。

标签: xml xslt xpath xslt-1.0


【解决方案1】:

您应该阅读“Muenchian grouping”,michael.hor257k 已经为您提供了指导。 (网络搜索会找到很多其他的。)

Muenchian 分组的作用是让您原则上可以在没有它的情况下做的更快。在某些情况下,增加的速度会在“原则上可行”和“实践中可行”之间产生差异。但在某些情况下,对这个问题采取简单的方法就足够了。

问题 1:对于每个不同的“ID”,您只希望在输出中有一个“条形”元素。 (请注意,您的示例输出表明您的描述是错误的:您不希望“只有具有唯一 'id' 的条形图”,因为 ID 为 1111 或 2222 的条形图在输入中都没有唯一 ID。您需要单个输出对于“id”的每个不同值。不是一回事。)

解决这个问题的一种方法:为“bar”编写两个模板,一个在给定“id”第一次出现时触发(实际上是在寻找最大的有效性/日期值),另一个这会导致所有以后出现的带有该 'id' 的 'bar' 被忽略。

<xsl:template match="bar" priority="10.0">
   <!--* find the highest validity/date with this ID here,
       * do what needs to be done. *-->
   ...
</xsl:template>
<xsl:template match="bar[id = preceding-sibling::bar/id]"
              priority="20.0"/>

我已经给出了明确的优先级来警告未来的我我正在尝试一些聪明的东西(并防止未来的我通过改变匹配模式以改变相对优先级的方式搞砸它) .

另一种方法是在'bar'的模板内放置一个选择/何时。

<xsl:template match="bar">
  <xsl:variable name="id" select="string(id)"/>
  <xsl:choose>
    <xsl:when test="preceding::bar[id=$id]"/>
    <xsl:otherwise>
      <!--* this is the first of this ID, deal with this ID now *-->
      ...
    </
  </
</

第二种模式可以更容易地制定找到您实际想要复制到输出的“条形”元素所需的逻辑。您希望处理的不是每个 ID 的第一个实例,而是具有最高有效性/日期值的实例:

<xsl:template match="bar">
  <xsl:variable name="id" select="string(id)"/>

  <xsl:choose>
    <!--* the behavior of comparisons here requires a little
        * bit of standing on our heads.  We want this 'bar' if
        * its validity/date value is greater than or equal to
        * all other such values for this ID.  So first we filter
        * out all cases where there is a higher validity/date value
        * on another 'bar' with this ID. *-->
    <xsl:when test="validity/date &lt; //bar[id=$id]/validity/date"/>

    <!--* The 'otherwise' case handles situations where this
        * is the only 'bar' with this ID, or where there is no
        * higher validity/date value. *-->
    <xsl:otherwise>
      <xsl:copy-of select="."/>
    </
  </
</

如果这是在“可管理”输入上运行的一次性或很少运行的样式表,这可能足够快,并且这种模式可能比 Muenchian 分组更容易理解,除非您已经对键有很好的理解以及它们的用途。如果速度太慢,Muenchian 分组会向您展示完成同样事情的通常更快的方法。

[注意:答案的初始版本有一个maxdate 变量

<xsl:variable name="maxdate" 
              select="max(//bar[id=$id]/validity/date)"/>

并简单地将当前值与它进行比较:

<xsl:when test="validity/date = $maxdate">
  <xsl:copy-of select="."/>
</

但是 XPath 1.0 中唯一的聚合函数是 count() 和 sum()。我会说“看看这在 XSLT 2.0 中有多容易?”但是如果你在 2.0 中,整个事情就会像

<xsl:sequence select="for $v in distinct-values(//bar/id)
    for $max in max(//bar[id=$v]/validity/date)
    return //bar[id=$v and validity/date = $max]"/>

而 max() 函数在使事情变得如此简单方面确实扮演着相对温和的角色。]

【讨论】:

  • XSLT 1.0 中没有 max() 函数。
  • 完全正确。我最初的回答有误。现已修复(...除非我刚刚替换了其他错误)。
  • 我希望在看到 XSLT 2.0 如此简单之后可以使用它,但这不是我能控制的。感谢您的精彩解释。我的 XML 可以在 30-50 MB 的范围内,所以我继续使用 Muenchian 分组。
【解决方案2】:

按照建议,我想出了下面的 xslt,它似乎工作正常:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:key name="bars-by-id" match="foo/bar" use="id" />
    <xsl:template match="foo">
        <foo name="fooName">
            <xsl:for-each select="bar[count(. | key('bars-by-id', id)[1]) = 1]">
                <xsl:variable name="currentID" select="id" />
                <xsl:variable name="barsForID" select="key('bars-by-id', $currentID)"/>
                <xsl:copy-of select="$barsForID[not(../bar[id=$currentID]/validity/date > validity/date)]" />
            </xsl:for-each>
        </foo>
    </xsl:template>
</xsl:stylesheet>

谢谢你的建议,真的很有帮助。请随时纠正我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    • 2015-03-30
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    相关资源
    最近更新 更多