XPath的 - 选择两个节点之间的兄弟姐妹第一组 [英] XPath - Select first group of siblings between two nodes

查看:225
本文介绍了XPath的 - 选择两个节点之间的兄弟姐妹第一组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个小问题,使用XPath查询在C#中一些HTML文件时



好吧,首先这里有一个示例HTML:

 <表ID =theTable> 
<&TBODY GT;
< TR类=theClass描述>将< / TR>
< TR类=theClass描述> B< / TR>
将; TR→1&下; / TR>
将; TR→2&下; / TR>
将; TR→3&下; / TR>
将; TR→4&下; / TR>
将; TR> 5℃/ TR>
将; TR类=theClass描述>℃下/ TR>
< TR类=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 LT; / TR>
将; TR> 14所述; / TR>
将; TR> 15℃; / TR>
< TR类=theClass描述> E< / TR>
< TR类=theClass描述> F< / TR>
< TR> 16 LT; / TR>
< TR> 17< / TR>
< TR> 18 LT; / TR>
< TR> 19 LT; / TR>
将; TR> 20℃; / TR>
< TR> 21 LT; / TR>
< TR> 22℃; / TR>
< / TBODY>
< /表>

现在,我想要做的就是让只属于B和之间的那些元素。ç节点(1,2,3,4,5,)



下面是我试过到目前为止:

 使用系统; 
使用System.Xml.XPath;

命名空间测试
{
类测试
{
静态无效的主要(字串[] args)
{
XPathDocument的文档=新的XPathDocument(的test.xml);
XPathNavigator的NAV = doc.CreateNavigator();

Console.WriteLine(nav.Select(//表[@ ID ='theTable'] / TBODY / TR [前同辈:: TR [@类='theClass描述']和以下事项兄弟姐妹:: TR [@类='theClass描述']])计数)。
Console.WriteLine(nav.Select(//表[@ ID ='theTable'] / tbody的/ TR [前同辈:: TR [@类='theClass描述'] [2]和以下同胞:: TR [@类='theClass描述'] [4])计数)。

Console.ReadKey(真);
}
}
}

这代码,轧过以上HTML,输出19和5
所以只有第二个XPath表达式的作品,但只因为它搜索的有类= theClass描述两个元素之前的元素和4个后他们。



我的问题现在开始。我想写一个表达式将返回后,随之而来的元素只有第一组< TD类=theClass描述>< / TD> 标签,无论多少组跟随它。



如果我跑我的代码在这个HTML

 <表ID =theTable> 
<&TBODY GT;
< TR类=theClass描述>将< / TR>
< TR类=theClass描述> B< / TR>
将; TR→1&下; / TR>
将; TR→2&下; / TR>
将; TR→3&下; / TR>
将; TR→4&下; / TR>
将; TR> 5℃/ TR>
将; TR→6&下; / TR>
< / TBODY>
< /表>



将输出0和0。



所以这是没有好处的。



没有任何人有什么想法?



感谢您!


解决方案

现在,我想要做的是让只有那些之间
的元素 b C 节点




使用此XPath表达式

  /*/*/tr[.= b'] 
/以下同胞:: *
[计数(|。/ * / * / TR [='C'] /前同辈:: *)
=
计数(/ * / * / TR [。='C'] /前同辈:: *)
]

下面是一个XSLT - 基于验证

 < ;的xsl:样式版本=1.0
的xmlns:XSL =http://www.w3.org/1999/XSL/Transform>
< XSL:输出中省略的XML声明=YES缩进=YES/>

<的xsl:模板匹配=/>
< XSL:复制的选择=
/*/*/tr[.='B']
/以下同胞:: *
[计数(。 | [。='C'] / * / * / TR /前同辈:: *)
=
计数(/ * / * / TR /前同辈[='C'。] :: *)
]
/>
< / XSL:模板>
< / XSL:样式>



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

 <表ID =theTable> 
<&TBODY GT;
< TR类=theClass描述>将< / TR>
< TR类=theClass描述> B< / TR>
将; TR→1&下; / TR>
将; TR→2&下; / TR>
将; TR→3&下; / TR>
将; TR→4&下; / TR>
将; TR> 5℃/ TR>
将; TR类=theClass描述>℃下/ TR>
< TR类=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 LT; / TR>
将; TR> 14所述; / TR>
将; TR> 15℃; / TR>
< TR类=theClass描述> E< / TR>
< TR类=theClass描述> F< / TR>
< TR> 16 LT; / TR>
< TR> 17< / TR>
< TR> 18 LT; / TR>
< TR> 19 LT; / TR>
将; TR> 20℃; / TR>
< TR> 21 LT; / TR>
< TR> 22℃; / TR>
< / TBODY>
< /表>



XPath表达式求值和所选择的节点被复制到输出:

 < TR> 1 LT; / TR> 
将; TR→2&下; / TR>
将; TR→3&下; / TR>
将; TR→4&下; / TR>
将; TR> 5℃/ TR>



说明



下面我们简单地使用的 Kayessian公式节点集路口的:

  $ NS1 [ COUNT(|。$ NS2)=计数($ NS2)] 

在这里我们取代 $ NS1

  /*/*/tr[.='B'] 
/以下同胞:: *

和我们取代 $ NS2

  / * / * / TR [。 ='C'] /前同辈:: * 



第二个问题




我的问题现在开始。我想编写一个表达式,将
只返回元素的第一组之后走过了< TD
类=theClass描述>< / TD>
标签,不管多少群体其后




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

  / * / * / TR [@类='theClass描述'

以下同胞:: * [1] [自我:: TR [没有(@ *)]]
] [1]
/以下同胞:: $ TR b $ b〔不是(@ *)

计数(前同辈:: TR
[@类='theClass描述'

以下的同胞: :* [1] [自我:: TR [没有(@ *)]]
]

= 1
]

说明



这个选择以下所有的兄弟姐妹 TR 第一个 * / * / TR 元素的属性的字符串值theClass描述,其第一个下面的元素兄弟是 TR 的。没有属性。



这些选择 TR 元素也满足的条件有两个:1)他们不任何属性; 2)他们只有一个前置兄弟 TR 元素,它的属性的字符串值 theClass描述



这里是XSLT - 基于验证

 <的xsl:样式版本=1.0
的xmlns:XSL =http://www.w3.org/1999/XSL/Transform >
< XSL:输出中省略的XML声明=YES缩进=YES/>

<的xsl:模板匹配=/>
< XSL:/ * / * / TR [@类='theClass描述'

以下同胞复制的选择=
:: * [1] [自:: TR [没有(@ *)]]
] [1]
/以下同胞:: TR
[没有(@ *)

计数(前同辈:: TR
[@类='theClass描述'

以下同胞:: * [1] [自:: TR [不(@ *)]]
]

= 1
]
/>
< / XSL:模板>
< / XSL:样式>



当第二提供的XML文档应用:

 <表ID =theTable> 
<&TBODY GT;
< TR类=theClass描述>将< / TR>
< TR类=theClass描述> B< / TR>
将; TR→1&下; / TR>
将; TR→2&下; / TR>
将; TR→3&下; / TR>
将; TR→4&下; / TR>
将; TR> 5℃/ TR>
将; TR→6&下; / TR>
< / TBODY>
< /表>



再次想和正确选择要素是输出:



 < TR> 1 LT; / TR> 
将; TR→2&下; / TR>
将; TR→3&下; / TR>
将; TR→4&下; / TR>
将; TR> 5℃/ TR>
将; TR→6&下; / TR>


I ran into a little problem when using XPath to query some HTML files in C#.

Ok, first here's a sample 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>

Now, what I'm trying to do is to get only those elements that are between the B and C nodes (1,2,3,4,5,).

Here's what I tried so far:

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);
        }
    }
}

This code, ran over the above HTML, outputs 19 and 5. So only the second XPath expression works but that only because it searches for elements that have two elements with class=theClass before them and 4 after them.

My problem starts now. I want to write a single expression that will return only the first group of elements that come after a <td class="theClass"></td> tag, no matter how many more groups are following it.

If I run my code over this 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>

it will output 0 and 0.

So it's no good.

Does anybody have any ideas?

Thank you!

解决方案

Now, what I'm trying to do is to get only those elements that are between the B and C nodes

Use this single XPath expression:

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

Here is an XSLT - based verification:

<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>

when this transformation is applied on the first provided XML document:

<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>

the XPath expression is evaluated and the selected nodes are copied to the output:

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

Explanation:

Here we simply use the Kayessian formula for node-set intersection:

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

where we substituted $ns1 with:

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

and we substituted $ns2 with:

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

The second problem:

My problem starts now. I want to write a single expression that will return only the first group of elements that come after a <td class="theClass"></td> tag, no matter how many more groups are following it.

Again a single XPath expression selecting those elements exists:

   /*/*/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
               ]

Explanation:

This selects all following siblings tr elements (that satisfy a number of conditions) of the first */*/tr element whose class attribute has string value "theClass" and whose first following element sibling is a tr that has no attributes.

The conditions that these selected tr elements also satisfy are two: 1) they don't have any attributes; and 2) they have only one preceding sibling tr element, whose class attribute has string value "theClass".

And here is the XSLT - based verification:

<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>

when applied on the second provided XML document:

<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>

again the wanted and correctly selected elements are output:

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

这篇关于XPath的 - 选择两个节点之间的兄弟姐妹第一组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆