【问题标题】:How would you find all nodes between two H3's using XPATH?如何使用 XPATH 找到两个 H3 之间的所有节点?
【发布时间】:2010-09-30 23:46:48
【问题描述】:

如何使用 XPATH 找到两个 H3 之间的所有节点?

【问题讨论】:

    标签: xpath


    【解决方案1】:

    在 XPath 1.0 中,一种方法是使用 Kayessian 方法进行节点集交集

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

    上面的表达式恰好选择同时属于节点集 $ns1 和节点集 $ns2 的节点。

    将此应用于特定问题——假设我们需要选择以下 XML 文档中第二个和第三个 h3 元素之间的所有节点:

    <html>
      <h3>Title T31</h3>
        <a31/>
        <b31/>
      <h3>Title T32</h3>
        <a32/>
        <b32/>
      <h3>Title T33</h3>
        <a33/>
        <b33/>
      <h3>Title T34</h3>
        <a34/>
        <b34/>
      <h3>Title T35</h3>
    </html>
    

    我们必须将$ns1替换为

    /*/h3[2]/following-sibling::node()
    

    并将$ns2 替换为

    /*/h3[3]/preceding-sibling::node()
    

    因此,完整的 XPath 表达式为

    /*/h3[2]/following-sibling::node()
                 [count(.|/*/h3[3]/preceding-sibling::node())
                 =
                  count(/*/h3[3]/preceding-sibling::node())
                 ]
    

    我们可以验证这是正确的 XPath 表达式:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/">
      <xsl:copy-of select=
       "/*/h3[2]/following-sibling::node()
                 [count(.|/*/h3[3]/preceding-sibling::node())
                 =
                  count(/*/h3[3]/preceding-sibling::node())
                 ]
       "/>
     </xsl:template>
    </xsl:stylesheet>
    

    当对上述 XML 文档应用此转换时,会产生所需的正确结果

    <a32/>
    
    <b32/>
    

    二。 XPath 2.0 解决方案

    使用intersect 运算符

       /*/h3[2]/following-sibling::node()
    intersect
       /*/h3[3]/preceding-sibling::node()
    

    【讨论】:

    • 所以这个不能解决的一个用例是最后一个 H3 之后的内容。我很好奇需要什么修改才能把它想象出来。
    • @klumsy:只需在现有表达式前面加上"/*/h3[2]/following-sibling::node()[not(/*/h3[3])] |
    • 如何在更多“切片”上循环使用这个表达式?如何在所有 h3 的循环中用变量替换 2 和 3?
    • @zypro,这很简单:拥有变量$startInd$endInd,并在您的“循环”中用必要的值声明它们。此外,将表达式“2”替换为$startInd,将“3”替换为$endInd。 XPath 2.0 表达式甚至可以是这样的:for $i in 1 to count(/*/h3) -1 return /*/h3[$i]/following-sibling::node() intersect /*/h3[$i+1]/preceding-sibling::node()
    【解决方案2】:

    当您知道两个标记是相同元素时的其他 XPath 1.0 解决方案(本例为 h3):

    /html/body/h3[2]/following-sibling::node()
                               [not(self::h3)]
                               [count(preceding-sibling::h3)=2]
    

    【讨论】:

      【解决方案3】:

      一个更通用的解决方案 - 在 XPath 2.0 中 - 假设您想要两个 h3 元素之间所有树深度的节点,这不一定是兄弟。

      /path/to/first/h3/following::node()[. << /path/to/second/h3]
      

      【讨论】:

        【解决方案4】:

        基于dimitre-novatchev 出色的答案,我可以采用以下解决方案,而不是为不同的 H3 硬编码 [2] 和 [3],我只给出第一项标题的内容。

        //h3[text()="Main Page Section Heading"]/following-sibling::node()
         [  count(.|//h3[text()="Main Page Section Heading"]/following-sibling::h3[1]/preceding-sibling::node()) =  
            count(//h3[text()="Main Page Section Heading"]/following-sibling::h3[1]/preceding-sibling::node())  ]
        

        我想要更进一步的地方是能够在我查看最后一个 H3 时处理该场景,并获得它之后的所有内容,在上述情况下,我无法获得最后一个H3。

        【讨论】:

          【解决方案5】:

          假设您的 &lt;h3&gt; 标记具有唯一属性(例如其文本或 id 属性),还有另一个很棒的通用解决方案:

          <xsl:key name="siblings_of_h3" match="*[not(self::h3)]" use="preceding-sibling::h3[1]/text()"/>
          
          <xsl:template match="h3">
            <!-- now select all tags belonging to the current h3 -->
            <xsl:apply-templates select="key('siblings_of_h3', text())"/>
          </xsl:template>
          

          它将所有标签按其前面的&lt;h3&gt;分组

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-05-22
            • 1970-01-01
            • 2021-09-19
            • 1970-01-01
            • 2012-12-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多