regulares - Encontrar la posición de un nodo usando xpath
xpath and xslt (8)
¿Alguien sabe cómo obtener la posición de un nodo usando xpath?
Digamos que tengo el siguiente xml:
<a>
<b>zyx</b>
<b>wvu</b>
<b>tsr</b>
<b>qpo</b>
</a>
Puedo usar la siguiente consulta xpath para seleccionar el tercer <b> nodo (<b> tsr </ b>):
a/b[.=''tsr'']
Lo cual está muy bien, pero quiero devolver la posición ordinal de ese nodo, algo así como:
a/b[.=''tsr'']/position()
(¡pero un poco más funcionando!)
¿Es posible?
editar : Olvidé mencionar estoy usando .net 2 así que es xpath 1.0!
Actualización : terminó usando la excelente respuesta de James Sulak . Para aquellos que están interesados aquí está mi implementación en C #:
int position = doc.SelectNodes("a/b[.=''tsr'']/preceding-sibling::b").Count + 1;
// Check the node actually exists
if (position > 1 || doc.SelectSingleNode("a/b[.=''tsr'']") != null)
{
Console.WriteLine("Found at position = {0}", position);
}
A diferencia de lo que se dijo previamente ''hermano anterior'' es realmente el eje que se debe usar, no ''precedente'', que hace algo completamente diferente, selecciona todo en el documento que está antes de la etiqueta de inicio del nodo actual. (vea http://www.w3schools.com/xpath/xpath_axes.asp )
El problema es que la posición del nodo no significa mucho sin un contexto.
El siguiente código le dará la ubicación del nodo en sus nodos hijos principales
using System;
using System.Xml;
public class XpathFinder
{
public static void Main(string[] args)
{
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(args[0]);
foreach ( XmlNode xn in xmldoc.SelectNodes(args[1]) )
{
for (int i = 0; i < xn.ParentNode.ChildNodes.Count; i++)
{
if ( xn.ParentNode.ChildNodes[i].Equals( xn ) )
{
Console.Out.WriteLine( i );
break;
}
}
}
}
}
Hago muchas cosas sobre Novell Identity Manager, y XPATH en ese contexto se ve un poco diferente.
Suponga que el valor que está buscando está en una variable de cadena, llamada TARGET, entonces el XPATH sería:
count(attr/value[.=''$TARGET'']/preceding-sibling::*)+1
Además, se señaló que para ahorrar algunos caracteres de espacio, lo siguiente también funcionaría:
count(attr/value[.=''$TARGET'']/preceding::*) + 1
También publiqué una versión más bonita de esto en Cool Solutions de Novell: Usando XPATH para obtener el nodo de posición
Me doy cuenta de que la publicación es antigua ... pero ...
reemplazar el asterisco con el nombre del nodo le daría mejores resultados
count(a/b[.=''tsr'']/preceding::a)+1.
en lugar de
count(a/b[.=''tsr'']/preceding::*)+1.
Puedes hacer esto con XSLT pero no estoy seguro acerca de XPath directo.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"
omit-xml-declaration="yes"/>
<xsl:template match="a/*[text()=''tsr'']">
<xsl:number value-of="position()"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Si alguna vez actualiza a XPath 2.0, tenga en cuenta que proporciona el index-of función, resuelve el problema de esta manera:
index-of(//b, //b[.=''tsr''])
Dónde:
- El primer parámetro es la secuencia para buscar
- 2.º es qué buscar
Solo una nota a la respuesta hecha por James Sulak.
Si desea tener en cuenta que el nodo puede no existir y desea mantenerlo puramente XPATH, intente lo siguiente que devolverá 0 si el nodo no existe.
count(a/b[.=''tsr'']/preceding-sibling::*)+number(boolean(a/b[.=''tsr'']))
Tratar:
count(a/b[.=''tsr'']/preceding-sibling::*)+1.