So finden Sie die nächste Übereinstimmung vom aktuellen Kontextknoten

c# html-agility-pack xpath

Frage

Ich habe eine ziemlich große XML-Datei, die ich mit einer C # -Anwendung und dem HtmlAgilityPack analysieren möchte. Das XML sieht ungefähr so ​​aus:

...
<tr>
<td><b>ABC-123</b></td>
<td>15</td>
<td>4</td>
</tr>
<tr>
<td>AB-4-320</td>
<td>11</td>
<td>2</td>
</tr>
<tr>
<td><b>ABC-123</b></td>
<td>15</td>
<td>4</td>
</tr>
<tr>
<td>AB-4-320</td>
<td>11</td>
<td>2</td>
</tr>
<tr>
<td>CONTROLLER1</td>
<td>4</td>
<td>3</td>
</tr>
<td>CONTROLLER2</td>
<td>4</td>
<td>3</td>
</tr>
...

Im Grunde eine Reihe von Tabellenzeilen und Spalten, die sich wiederholen. Ich suche zuerst nach einem Controller mit:

string xPath = @"//tr/td[starts-with(.,'CONTROLLER2')]";
HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes(xPath);
foreach (HtmlNode link in nodes) { ... }

Welche gibt den richtigen Knoten zurück. Nun möchte ich rückwärts (aufwärts) nach dem ersten (nächsten) passenden <td> -Knoten suchen, der mit dem Text "ABC" beginnt:

string xPath = @link.XPath + @"/parent::tr/preceding-sibling::tr/td[starts-with(.,'ABC-')]";

Dies gibt alle übereinstimmenden Knoten zurück, nicht nur den nächsten. Als ich versuchte, [1] am Ende dieser XPath-Zeichenfolge hinzuzufügen, schien es nicht zu funktionieren und ich habe keine Beispiele gefunden, die ein Prädikat zeigen, das mit einer solchen Achsenfunktion verwendet wird. Oder, wahrscheinlicher, ich mache es falsch. Irgendwelche Vorschläge?

Akzeptierte Antwort

Sie können diesen XPath verwenden:

/parent::tr/preceding-sibling::tr[td[starts-with(.,'ABC-')]][1]/td[starts-with(.,'ABC-')]

Das sucht nach dem nächsten vorhergehenden <tr> , der child <td> beginnt mit "ABC-". Dann hol <td> das bestimmte <td> -Element.

Es gibt mindestens zwei Ansätze, die Sie bei der Verwendung von HtmlAgilityPack auswählen können:

foreach (HtmlNode link in nodes)
{
    //approach 1 : notice dot(.) at the beginning of the XPath
    string xPath1 = 
        @"./parent::tr/preceding-sibling::tr[td[starts-with(.,'ABC-')]][1]/td[starts-with(.,'ABC-')]";
    var n1 = node.SelectSingleNode(xPath1);
    Console.WriteLine(n1.InnerHtml);

    //approach 2 : appending to XPath of current link
    string xPath2 = 
        @"/parent::tr/preceding-sibling::tr[td[starts-with(.,'ABC-')]][1]/td[starts-with(.,'ABC-')]";
    var n2 = node.SelectSingleNode(link.XPath + xPath2);
    Console.WriteLine(n2.InnerHtml);
}

Beliebte Antwort

Wenn Sie LINQ-to-XML anstelle von HAP verwenden können, funktioniert dies:

var node = xml.Root.Elements("tr")
    .TakeWhile(tr => !tr.Elements("td")
        .Any(td => td.Value.StartsWith("CONTROLLER2")))
    .SelectMany(tr => tr.Elements("td"))
    .Where(td => td.Value.StartsWith("ABC-"))
    .Last();

Ich habe dieses Ergebnis:

<td>
  <b>ABC-123</b>
</td>

(Was ich überprüft habe, war der zweite übereinstimmende Knoten in Ihrer Stichprobe, nicht der erste.)



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum