【问题标题】:Finding minimum value using XPath 1.0 does not work使用 XPath 1.0 查找最小值不起作用
【发布时间】:2014-09-22 20:47:24
【问题描述】:

我试图从 XML 文档(它实际上是一个转换为 XML 的 HTML 表)中找到某个元素的最小值。但是,这并没有按预期工作。

该查询类似于How can I use XPath to find the minimum value of an attribute in a set of elements? 中使用的查询。它看起来像这样:

/table[@id="search-result-0"]/tbody/tr[
    not(substring-before(td[1], " ") > substring-before(../tr/td[1], " "))
]

在示例 XML 上执行

<table class="tablesorter" id="search-result-0">
    <thead>
        <tr>
            <th class="header headerSortDown">Preis</th>
            <th class="header headerSortDown">Zustand</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td width="45px">15 CHF</td>
            <td width="175px">Ausgepack und doch nie gebraucht</td>
        </tr>
        <tr>
            <td width="45px">20 CHF</td>
            <td width="175px">Ausgepack und doch nie gebraucht</td>
        </tr>
        <tr>
            <td width="45px">25 CHF</td>
            <td width="175px">Ausgepack und doch nie gebraucht</td>
        </tr>
        <tr>
            <td width="45px">35 CHF</td>
            <td width="175px">Ausgepack und doch nie gebraucht</td>
        </tr>
        <tr>
            <td width="45px">14 CHF</td>
            <td width="175px">Gebraucht, aber noch in Ordnung</td>
        </tr>
        <tr>
            <td width="45px">15 CHF</td>
            <td width="175px">Gebraucht, aber noch in Ordnung</td>
        </tr>
        <tr>
            <td width="45px">15 CHF</td>
            <td width="175px">Gebraucht, aber noch in Ordnung</td>
        </tr>
    </tbody>
</table>

查询返回以下结果:

<tr>
<td width="45px">15 CHF</td>
<td width="175px">Ausgepack und doch nie gebraucht</td>
</tr>
-----------------------
<tr>
<td width="45px">14 CHF</td>
<td width="175px">Gebraucht, aber noch in Ordnung</td>
</tr>
-----------------------
<tr>
<td width="45px">15 CHF</td>
<td width="175px">Gebraucht, aber noch in Ordnung</td>
</tr>
-----------------------
<tr>
<td width="45px">15 CHF</td>
<td width="175px">Gebraucht, aber noch in Ordnung</td>
</tr>

为什么返回的节点多于一个?因为只有一个最小值,所以应该只返回一个节点。有人看到查询有什么问题吗?它应该只返回包含14 CHF 的节点。

使用http://xpath.online-toolz.com/tools/xpath-editor.php获得的结果

【问题讨论】:

    标签: xml xpath xpath-1.0


    【解决方案1】:

    TML 已经指出了为什么您当前的路径表达式不起作用,但没有提出可行的替代方案。

    原因很简单,正如@Tomalak 所说:

    我同意马蒂亚斯的观点。如果不更改输入 XML,这在 XPath 1.0 中实际上是不可能的。

    我添加这个答案是为了详细说明您必须在搜索最少 CHF 之前预处理 XML 的方式。请记住:这非常复杂,因为您要求在 XPath 1.0 中提供解决方案。使用 XPath 2.0,您的问题可以通过单个路径表达式来解决。


    XML 设计

    我认为您的问题说明了为什么在使用 XML 时 XML 设计实际上是必不可少的。 为什么?因为您的问题归结为以下几点:您的 XML 的设计方式使得操作内容变得困难。更准确地说,在这样的 td 元素中:

    <td width="45px">15 CHF</td>
    

    td 元素的文本节点中包含金额(作为数字)和货币。如果您的 XML 输入以更聪明或更规范的方式设计,它看起来像:

    <td width="45px" currency="CHF">15</td>
    

    看到区别了吗?现在,不同种类的内容已经很明显的分开了。


    XPath 修订版

    假设在新设计的 XML 中,tr/td[1] 元素的唯一内容是数字,那么您使用的 Pavel Minaev 的 XPath 表达式可以工作:

    /table[@id="search-result-0"]/tbody/tr[not(td[1] > ../tr/td[1])][1]
    

    XML 结果(用the tool you use 测试)

    <tr>
    <td width="45px">14</td>
    <td width="175px">Ausgepack und doch nie gebraucht</td>
    </tr>
    

    为什么Pavel's expression不起作用,仅仅因为我添加了substring-before

    您自己已经找到了部分答案。它与 XPath 1.0 函数中如何处理项目序列有关。

    substring-before() 是一个 XPath 1.0 函数,它需要两个参数,它们都是字符串。而且,最重要的是,如果您将 字符串序列 定义为 substring-before() 的第一个参数,则只有 第一个字符串 将被处理,其他字符串将被忽略。

    Pavel 的回答,适应了这个问题:

    tr[not(td[1] > ../tr/td[1])][1]
    

    依赖于表达式的第二部分../tr/td[1] 找到所有tbody 的所有tr 元素的第一个td 子元素。不涉及函数,序列作为&gt;的操作数也没有错。

    如果我们需要substring-before(),因为文本内容实际上既是数字(我们想要)又是货币(我们想要忽略),我们必须将它包裹在表达式的两个部分:

    tr[not(substring-before(td[1],' ') > substring-before(../tr/td[1],' '))][1]
    

    &gt;左侧没有问题,因为当前tr只有一个td[1]。但是在右边,有一个序列节点,即../tr/td[1]。可悲的是,substring-before() 只能处理其中的第一个

    查看@TML 的答案以了解其后果。

    【讨论】:

    • 伟大的扩展和细节,马蒂亚斯。
    • 我明白了。由于无法更改源文档,我想出了一个 XSLT 解决方案(请参阅我的答案)。
    【解决方案2】:

    您在此处使用的 XPath 查询只会在没有重复值的情况下找到“最小值”,并且在将值写入节点之前对其进行排序;这是因为它只是将当前值substring-before(td[1], " ") 与找到的第一个值substring-before(../tr/td[1], " ") 进行比较。分解比较:

    [1] not(15 > 15)
    [2] not(20 > 15)
    [3] not(25 > 15)
    [4] not(35 > 15)
    [5] not(14 > 15)
    [6] not(15 > 15)
    [7] not(15 > 15)
    

    比较 1、5、6 和 7 的结果为真(左侧不大于右侧)。

    【讨论】:

    • 你是对的。在节点集上调用函数仅返回第一个节点的结果,而不是再次返回集。有关如何解决此问题的任何建议?
    • @str 我想说这在 XPath 1.0 中是不可能的。你能事先操纵元素吗?如果 substring-before 可以是在应用 XPath 表达式之前执行的单独步骤(这样会留下 &lt;td width="45px"&gt;15&lt;/td&gt;) - 那么我有一个适合您的解决方案。
    • 我同意马蒂亚斯的观点。在不更改输入 XML 的情况下,这实际上是在 XPath 1.0 中是不可能的。
    【解决方案3】:

    与此同时,我决定改用 XSLT。这是我想出的样式表:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
    
        <xsl:output method="text" omit-xml-declaration="yes" indent="no" encoding="UTF-8"/>
        <xsl:strip-space elements="*"/> 
    
        <xsl:template match="//table[@id=\'search-result-0\']/tbody">
            <ul>
                <xsl:for-each select="tr/td[@width=\'45px\']">
                    <xsl:sort select="substring-before(., \' \')" data-type="number" order="ascending"/>
    
                    <xsl:if test="position() = 1">
                         <xsl:value-of select="substring-before(., \' \')"/>
                    </xsl:if>
                </xsl:for-each>
            </ul>
        </xsl:template>
    
        <xsl:template match="text()"/> <!-- ignore the plain text -->
    
    </xsl:stylesheet>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-25
      • 2018-12-17
      • 1970-01-01
      • 1970-01-01
      • 2018-08-14
      相关资源
      最近更新 更多