[英]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。
所以这不好。
有人有什么想法吗?
谢谢!
现在,我要做的是仅获取
B
和C
节点之间的那些元素
使用以下单个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.