XPATH选择不同的值

时间:2014-06-30 12:21:22

标签: xml xpath distinct

我想在两个属性(宽度或高度)中的一个是不同的时候选择页面。

这是我的XML:

<pages>
    <page id="page0001" height="1000" width="960"></page>
    <page id="page4931" height="1000" width="960"></page>
    <page id="page6342" height="1000" width="961"></page>
    <page id="page7995" height="1001" width="961"></page>
    <page id="page4461" height="1001" width="960"></page>
    <page id="page4690" height="1001" width="960"></page>
    <page id="page9001" height="1001" width="961"></page>
</pages>

这是我的选择

<xsl:for-each select="/prototype/pages/page[@width != (preceding-sibling::*/@width) or @height != (preceding-sibling::*/@height) or position() = 1]">
    <div class="{@width}x{@height} {@id}"></div>
</xsl:for-each>

结果如下:

<div class="960x1000 page0001"></div>
<div class="961x1000 page6342"></div>
<div class="961x1001 page7995"></div>
<div class="960x1001 page4461"></div>
<div class="960x1001 page4690"></div>
<div class="961x1001 page9001"></div>

我期望拥有什么

<div class="960x1000 page0001"></div>
<div class="961x1000 page6342"></div>
<div class="961x1001 page7995"></div>
<div class="960x1001 page4461"></div>

我知道我的选择不正确,我理解为什么。有没有可能修改它以获得正确的结果?

1 个答案:

答案 0 :(得分:1)

问题是XPath中的=!=等比较运算符是隐式存在量化的,换句话说

@width != preceding-sibling::*/@width

检查是否有任何之前的兄弟,其宽度与我的不同。在这种情况下你真正想要的是这个节点的宽度和高度的组合与它的任何前辈的宽度和高度的组合不同。您可以使用for-each覆盖所有页面,并在for-each

的正文中进行测试
<xsl:for-each select="/prototype/pages/page">
    <xsl:variable name="myWidth" select="@width" />
    <xsl:variable name="myHeight" select="@height" />
    <xsl:if test="not(preceding-sibling::*[@height=$myHeight][@width=$myWidth])">
        <div class="{@width}x{@height} {@id}"></div>
    </xsl:if>
</xsl:for-each>

或者,您可以将其视为分组问题 - 按照page元素的宽度和高度对其进行分组,并为每个组输出一个div。在XSLT 2.0中,这可能是

<xsl:for-each-group select="/prototype/pages/page"
      group-by="concat(@width, 'x', @height)">
    <div class="{current-grouping-key()} {@id}" />
</xsl:for-each-group>

在1.0中你可以使用 Muenchian分组方法获得相同的效果(你可以通过搜索功能找到很多其他问题来解释它,所以我不会再重复一次这里)。