我需要XPath 1.0中的帮助来过滤下面的XML,这样我才会得到具有不同“id”且具有最高'valid / date'的条形码:
<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>
答案 0 :(得分:1)
你应该阅读“Muenchian grouping”,michael.hor257k已经给你一个指针。 (网络搜索会发现很多其他人。)
Muenchian分组所做的是,如果没有它,原则上可以做得更快。在某些情况下,增加的速度使“原则上可能”和“在实践中可行”之间存在差异。但在某些情况下,解决这个问题的简单方法就足够了。
问题1:您只需在输出中为每个不同的“ID”设置一个“bar”元素。 (请注意,您的示例输出显示您的描述错误:您不希望“只有具有唯一'id'的栏”,因为ID为1111或2222的条都没有输入中的唯一ID。您需要单个输出对于'id'的每个不同值。不一样。)
解决这个问题的一种方法:为'bar'写两个模板,一个为第一次出现给定'id'而触发(实际上是找到最大有效性/日期值的工作),另一个是这会导致以后出现的'bar'与'id'被忽略。
<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 < //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()函数在使事情变得如此简单方面起到了相对温和的作用。]
答案 1 :(得分:0)
正如所建议的那样,我想出了下面的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>
感谢您的建议,这确实有所帮助。请随时纠正我。