繁体   English   中英

XPath-选择两个节点之间的第一组兄弟姐妹

[英]XPath - Select first group of siblings between two nodes

使用XPath查询C#中的某些HTML文件时遇到了一个小问题。

好的,首先这里是一个示例HTML:

<table id="theTable">
    <tbody>
        <tr class="theClass">A</tr>
        <tr class="theClass">B</tr>
        <tr>1</tr>
        <tr>2</tr>
        <tr>3</tr>
        <tr>4</tr>
        <tr>5</tr>
        <tr class="theClass">C</tr>
        <tr class="theClass">D</tr>
        <tr>6</tr>
        <tr>7</tr>
        <tr>8</tr>
        <tr>9</tr>
        <tr>10</tr>
        <tr>11</tr>
        <tr>12</tr>
        <tr>13</tr>
        <tr>14</tr>
        <tr>15</tr>
        <tr class="theClass">E</tr>
        <tr class="theClass">F</tr>
        <tr>16</tr>
        <tr>17</tr>
        <tr>18</tr>
        <tr>19</tr>
        <tr>20</tr>
        <tr>21</tr>
        <tr>22</tr>
    </tbody>
</table>

现在,我想做的是仅获取B和C节点(1、2、3、4、5,)之间的那些元素。

这是我到目前为止所尝试的:

using System;
using System.Xml.XPath;

namespace Test
{
    class Test
    {
        static void Main(string[] args)
        {
            XPathDocument doc = new XPathDocument("Test.xml");
            XPathNavigator nav = doc.CreateNavigator();

            Console.WriteLine(nav.Select("//table[@id='theTable']/tbody/tr[preceding-sibling::tr[@class='theClass'] and following-sibling::tr[@class='theClass']]").Count);
            Console.WriteLine(nav.Select("//table[@id='theTable']/tbody/tr[preceding-sibling::tr[@class='theClass'][2] and following-sibling::tr[@class='theClass'][4]]").Count);

            Console.ReadKey(true);
        }
    }
}

这段代码在上述HTML上运行,输出19和5。因此,仅第二个XPath表达式有效,但这仅是因为它搜索的元素具有两个元素,它们前面是class=theClass ,后面是4。

我的问题现在开始了。 我想编写一个表达式,该表达式仅返回<td class="theClass"></td>标记之后的第一组元素,而不管后面跟随着多少组。

如果我在此HTML上运行代码

<table id="theTable">
    <tbody>
        <tr class="theClass">A</tr>
        <tr class="theClass">B</tr>
        <tr>1</tr>
        <tr>2</tr>
        <tr>3</tr>
        <tr>4</tr>
        <tr>5</tr>
        <tr>6</tr>
    </tbody>
</table>

它将输出0和0。

所以这不好。

有人有什么想法吗?

谢谢!

现在,我要做的是仅获取BC节点之间的那些元素

使用以下单个XPath表达式

   /*/*/tr[.='B']
           /following-sibling::*
             [count(.|/*/*/tr[. ='C']/preceding-sibling::*)
             =
              count(/*/*/tr[. ='C']/preceding-sibling::*)
             ]

这是基于XSLT的验证

<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=
  "/*/*/tr[.='B']
           /following-sibling::*
             [count(.|/*/*/tr[. ='C']/preceding-sibling::*)
             =
              count(/*/*/tr[. ='C']/preceding-sibling::*)
             ]
  "/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于第一个提供的XML文档时:

<table id="theTable">
    <tbody>
        <tr class="theClass">A</tr>
        <tr class="theClass">B</tr>
        <tr>1</tr>
        <tr>2</tr>
        <tr>3</tr>
        <tr>4</tr>
        <tr>5</tr>
        <tr class="theClass">C</tr>
        <tr class="theClass">D</tr>
        <tr>6</tr>
        <tr>7</tr>
        <tr>8</tr>
        <tr>9</tr>
        <tr>10</tr>
        <tr>11</tr>
        <tr>12</tr>
        <tr>13</tr>
        <tr>14</tr>
        <tr>15</tr>
        <tr class="theClass">E</tr>
        <tr class="theClass">F</tr>
        <tr>16</tr>
        <tr>17</tr>
        <tr>18</tr>
        <tr>19</tr>
        <tr>20</tr>
        <tr>21</tr>
        <tr>22</tr>
    </tbody>
</table>

将评估XPath表达式并将所选节点复制到输出:

<tr>1</tr>
<tr>2</tr>
<tr>3</tr>
<tr>4</tr>
<tr>5</tr>

说明

在这里,我们仅将Kayessian公式用于节点集相交

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

我们在其中将$ns1替换$ns1

 /*/*/tr[.='B']
               /following-sibling::*

然后我们将$ns2替换$ns2

/*/*/tr[. ='C']/preceding-sibling::*

第二个问题

我的问题现在开始了。 我想编写一个表达式,该表达式仅返回<td class="theClass"></td>标记之后的第一组元素,而不管后面跟随着多少组。

再次存在一个选择这些元素的XPath表达式

   /*/*/tr[@class='theClass'
         and
           following-sibling::*[1][self::tr[not(@*)] ]
           ][1]
             /following-sibling::tr
               [not(@*)
              and
                count(preceding-sibling::tr
                       [@class='theClass'
                      and
                        following-sibling::*[1][self::tr[not(@*)] ]
                       ]
                     )
                = 1
               ]

说明

这将选择第一个*/*/tr元素的所有后续同胞tr元素(满足多个条件),这些元素的class属性具有字符串值"theClass"并且其后继的第一同胞兄弟元素是没有属性的tr

这些选定的tr元素还满足的条件是两个:1)它们没有任何属性; 和2)它们只有一个在前的同级tr元素,其class属性具有字符串值"theClass"

这是基于XSLT的验证

<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=
  "/*/*/tr[@class='theClass'
         and
           following-sibling::*[1][self::tr[not(@*)] ]
           ][1]
             /following-sibling::tr
               [not(@*)
              and
                count(preceding-sibling::tr
                       [@class='theClass'
                      and
                        following-sibling::*[1][self::tr[not(@*)] ]
                       ]
                     )
                = 1
               ]
  "/>
 </xsl:template>
</xsl:stylesheet>

当应用于第二个提供的XML文档时

<table id="theTable">
    <tbody>
        <tr class="theClass">A</tr>
        <tr class="theClass">B</tr>
        <tr>1</tr>
        <tr>2</tr>
        <tr>3</tr>
        <tr>4</tr>
        <tr>5</tr>
        <tr>6</tr>
    </tbody>
</table>

再次输出所需和正确选择的元素:

<tr>1</tr>
<tr>2</tr>
<tr>3</tr>
<tr>4</tr>
<tr>5</tr>
<tr>6</tr>

如果您不必使用XPath,则某些LINQ可能更容易正确使用,并且更具可读性。

在您的情况下,类似于以下伪代码,可以使用Skip和TakeWhile的组合:

nav.Select("//table[@id='theTable']/tbody/tr") // whatever to get list of all TR
   .Skip("theClass is B") // some condition to skip up to first node
   .TakeWhile("theClass is C"); // some condition to take upto second node.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM