2012-05-30 94 views
2

使用XPath查詢C#中的某些HTML文件時遇到了一些小問題。XPath - 選擇兩個節點之間的第一組兄弟

好吧,首先這裏有一個示例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

所以它沒有好。

有沒有人有任何想法?

謝謝!

+0

我不會跟着你。預期的結果是什麼? –

+0

@ChuckSavage對於第一個HTML,我期望返回元素1,2,3,4,5和第二個HTML元素1,2,3,4,5,6。 –

回答

6

現在,我想要做的是讓只之間的是 這些元素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式爲節點集合相交

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

和:

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

在那裏我們與取代$ns1我們

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

第二個問題:與取代$ns2

我的問題現在開始。我想寫一個表達式, 只返回在<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",並且其第一個後續的el ement sibling是沒有屬性的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> 
+0

非常感謝,它的工作原理。你能否留下最後表達的解釋?我不確定我是否理解它。 謝謝! –

+0

@LeifLazar:不客氣。我編輯了答案併爲兩個表達式添加了解釋。 –

1

如果你不需要使用XPath,一些LINQ可能會更容易得到正確的並且更易讀。

在跳躍中你的情況結合,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. 
相關問題