Cómo encontrar la coincidencia más cercana desde el nodo de contexto actual

c# html-agility-pack xpath

Pregunta

Tengo un archivo XML bastante grande que estoy intentando analizar utilizando una aplicación C # y el HtmlAgilityPack. El XML se ve algo como esto:

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

Básicamente una serie de filas de tablas y columnas que se repiten. Primero hago una búsqueda de un controlador usando:

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

Lo que devuelve el nodo correcto. Ahora quiero buscar hacia atrás (arriba) el primer nodo <td> coincidente (más cercano) que comienza con el texto "ABC":

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

Esto devuelve todos los nodos coincidentes, no solo el más cercano. Cuando intenté agregar [1] al final de esta cadena XPath, no pareció funcionar y no he encontrado ningún ejemplo que muestre que se esté utilizando un predicado con una función de ejes como esta. O, más probablemente, lo estoy haciendo mal. ¿Alguna sugerencia?

Respuesta aceptada

Puedes usar este XPath:

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

Eso buscará el <tr> anterior más cercano que tiene <td> hijo comienza con 'ABC-'. A continuación, obtenga ese elemento <td> particular.

Hay al menos dos enfoques que puede elegir al usar HtmlAgilityPack:

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

Respuesta popular

Si puede usar LINQ-to-XML en lugar del HAP, esto funciona:

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

Obtuve este resultado:

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

(Lo que verifiqué fue el segundo nodo coincidente en su muestra, no el primero).




Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué