2

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を出力します。したがって、2番目のXPath式のみが機能しますが、これは、class=theClass前に2つの要素があり、後に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を出力します。

だからそれは良くない。

誰かアイデアはありますか?

ありがとうございました!

4

2 に答える 2

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式を使用します。

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

置換$ns1した場所:

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

そして、次のように置き換え$ns2ました。

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

2番目の問題

私の問題は今始まります。タグの後に続く要素の最初のグループのみを返す単一の式を作成したいと思います。これは、<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要素(いくつかの条件を満たす)が選択されます。*/*/trclass"theClass"tr

これらの選択されたtr要素も満たす条件は2つです。1)属性がない。2)属性が文字列値を持つ先行する兄弟tr要素が1つだけあります。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>

2番目に提供された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>
于 2012-05-30T04:41:20.593 に答える
1

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.
于 2012-05-30T01:02:12.700 に答える