【问题标题】:XPath 1 queryXPath 1 查询
【发布时间】:2009-02-13 19:52:15
【问题描述】:

我发布了一个类似的问题,并得到了非常有用的回复。 现在问题有点不同,所以我发布它。 我指定它是一个与 XPath 1 相关的问题。 这是我的 XML 文件的内容:

<?xml version="1.0" encoding="ISO-8859-1"?>
<mainNode>
    <subNode>
        <h hm="08:45">
            <store id="1563">Open</store>
        </h>
        <h hm="13:00">
            <store id="1045">Open</store>
            <store id="763">Open</store>
            <store id="1047">Open</store>
        </h>
        <h hm="16:30">
            <store id="1045">Open</store>
            <store id="763">Open</store>
            <store id="1047">Open</store>
        </h>
        <h hm="20:00">
            <store id="1045">Open</store>
            <store id="1043">Open</store>
            <store id="1052">Open</store>
        </h>
        <h hm="22:00">
            <store id="763">Open</store>
        <store id="1052">Open</store>
        </h>
    </subNode>
</mainNode>

我的程序获取当前时间:如果我得到12.40,我必须检索下一个h hm(13.00)的所有stores id:这个问题已经解决了。

检索数据后,通过第二个 XPath 查询,我必须得到直到在当天(XML 文件是其代表),单个商店将打开。 因此,假设商店是标识为 id=1045 的商店,现在是早上 12.40。该商店将在 20.00 关闭,因为它位于 h hm=13.00 子节点、h hm=16.30 子节点和 h hm=20.00 子节点中。所以,我必须得到那个 20.00。

案例 2:现在是 12.40,我必须知道 763 什么时候关闭。它将在 16.30 关闭,无论它是否包含在最后一个节点中 (h hm=22.00)。所以,我必须得到那个 16.30。

这可能吗?

提前致谢。

【问题讨论】:

  • 亲爱的,我做了一些连续的编辑。现在解决方案是正确的,可以查看了。玩得开心:)
  • @vyger 是的,你是对的,我在我的回答中修复了最后一个缺失的部分。谢谢
  • @vyger 我的答案中的 XPath 表达式是您的第二个问题的解决方案(它也解决了第一个问题,这不是缺点:))
  • @Dimitre:对不起,我想要删除我的 cmets... :-) 对不起,我真的很累...无论如何:今天我知道他们想要改变很多我正在处理的应用程序。所以,可能也不再需要 XPath。
  • @Dimitre 和这里​​的每个人:我对每个答案都投了 +1,只是因为我不会使用任何答案。我同样感谢这里的所有开发人员抽出时间来帮助我。

标签: xpath


【解决方案1】:

下面是如何构造这样一个 XPatch 表达式:

以下 XPath 表达式选择想要的结果

($vOpen[not(count(following-sibling::h[1] | $vOpen) 
          = 
           count($vOpen))
       ][1]/@hm
|

  $vOpen[last()]/@hm

 )

  [1]

在哪里$vOpen

定义为:

$vge1240[store/@id=$vId]

而$vge1240定义为:

/*/*/h[translate(@hm,':','') >= 1240]

而$vId定义为:

1045

或

763

上述变量可以在 XSLT 样式表中定义和引用,或者,如果 XPath 嵌入在另一个主机中,则每个变量引用都必须替换为变量定义的右侧。在这种情况下,完整的 XPath 表达式将是:

   (/*/*/h[translate(@hm,':','') >= 1240][store/@id=763]
      [not(count(following-sibling::h[1]
                |             
                 /*/*/h[translate(@hm,':','') >= 1240][store/@id=763]
                 )     
          =       
           count(/*/*/h[translate(@hm,':','') >= 1240][store/@id=763])) 
          ]  
          [1]
            /@hm


     |
        /*/*/h[translate(@hm,':','') >= 1240][store/@id=763]
           [last()]
                   /@hm 
       )

        [1]

解释:

($vOpen[not(count(following-sibling::h[1] | $vOpen) 
          = 
           count($vOpen))
       ][1]/@hm
|

  $vOpen[last()]/@hm

 )

  [1]

意思如下:

  1. 来自“开放”时间中包含 id (763) 的所有条目

  2. 取那些紧随其后的兄弟不属于该集合的那些(封闭或不包含 763)

  3. 从那些拿第一个。

  4. 从上面步骤 3. 中选择的节点中获取第一个(按文档顺序)和 $vOpen 中的最后一个元素。如果其中的所有条目都包含给定的 ID,这将选择“开放”时间中的最后一个条目。

这里我们基本上使用 Kayesian 方法来进行两个节点集的交集 $ns1 和 $ns2:

$ns1[count(.|$ns2) = count($ns2)]

【讨论】:

  • 嗨,Dimitre,请注意:我在第一篇文章中解释的双查询设计应该保留。我只需要第二个查询。
  • @vyger 我的答案中的 XPath 表达式是您的第二个问题的解决方案(它也解决了第一个问题,这不是缺点:))
  • 你的意思是贝叶斯方法吗?
  • @mousio: 不,Kayesian -- 在 Michael Kay (@Michael Kay) 之后,他首先提出了用于两个节点集交集的 XPath 1.0 表达式。
  • 好的,现在我得到了真正的搜索结果 :] 谢谢!
【解决方案2】:

我将在您提到的最后一个question 中重复我对您的回答的最后一部分。

将 XML 加载到一些更有利于您的需求的数据结构中会更实用。这个次要问题只是再次强化了该建议的合理性。

【讨论】:

  • @AnthonyWJones:有理由不按照你的建议去做。那就是:你的建议是我能做的最后一件事。我并不是说它不是最好的,但最好的就是对你正在从事的项目更好的东西。
【解决方案3】:

我认为这行得通。它会找到 store/@id 的最后一次连续出现(在开始时间之后),其中@id = $id,我相信这就是您要寻找的。​​p>

<xsl:variable name="id" select="'1045'"/>
<xsl:variable name="st" select="translate('12:40',':','')"/>

...

((//h[translate(@hm,':','') >= $st])[position() = count(preceding-sibling::*[store/@id=$id])+1 and store/@id=$id])[last()]/@hm

说明: 首先,选择与提供的开始时间共享开始时间 >= 的所有元素。然后,在这些结果中,选择最后一个元素的 @hm 属性,该属性的位置等于其中包含请求的 store/@id 标记的前面元素的数量。

这个简短版本的一个限制是,如果商店 id 不在开始时间之后的第一个元素内出现,它将失败。下面的解决了这个限制,从包含正确 store/@id 的开始时间之后的第一个开始,但它有点混乱:

<xsl:variable name="id" select="'1045'"/>
<xsl:variable name="st" select="translate('12:40',':','')"/>

...

((//h[translate(@hm,':','') >= $st and position() >= count(//h[not(preceding-sibling::*[store/@id=$id])])])[position() = count(preceding-sibling::*[store/@id=$id])+1 and store/@id=$id])[last()]/@hm

【讨论】:

    【解决方案4】:

    如果您要查找的商店 ID 在 $store 中,这将获得最后一个 h/@hm,其中包含该 ID,后跟一个 h,不 包含它或包含该商店 ID 的最后一个 h/@hm(对于在最后一个 h/@hm 关闭的商店)。

    //h[not(store/@id=$store)]/preceding-sibling::h[store/@id = $store][1]/@hm | //h[store/@id=$store][last()]/@hm
    

    使用您的示例 XML 在 XSLT 中进行测试:

    <?xml version="1.0" encoding="utf-8" ?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
        <xsl:variable name="store" select="763"/>
    
        <xsl:template match="/">
            <closes>
                <xsl:value-of select="//h[not(store/@id=$store)]/preceding-sibling::h[store/@id = $store][1]/@hm | //h[store/@id=$store][last()]/@hm"/>
            </closes>
        </xsl:template>
    </xsl:stylesheet>
    

    打印&lt;closes&gt;16:30&lt;/closes&gt;。如果您将 $store 更改为 1052,则会打印 &lt;closes&gt;22:00&lt;/closes&gt;。

    【讨论】:

    • 感谢您抽出宝贵时间回答。一个问题(从这里我无法测试,我必须在星期一等待):如果 763 在最后一个节点中(这样它后面就没有任何东西了)怎么办?
    • fiddle, fiddle 为什么,它做正确的事,当然! ;-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-18
    • 2010-11-12
    • 1970-01-01
    相关资源
    最近更新 更多