XPath (XML Path Language) es un lenguaje que permite construir expresiones que recorren y procesan un documento XML. La idea es parecida a las expresiones regulares para seleccionar partes de un texto sin atributos (plain text). XPath permite buscar y seleccionar teniendo en cuenta la estructura jerárquica del XML. XPath fue creado para su uso en el estándar XSLT, en el que se usa para seleccionar y examinar la estructura del documento de entrada de la transformación. XPath fue definido por el consorcio W3C.
Todo el procesamiento realizado con un fichero XML está basado en la posibilidad de direccionar o acceder a cada una de las partes que lo componen, de modo que podamos tratar cada uno de los elementos de forma diferenciada.
El tratamiento del fichero XML comienza por la localización del mismo a lo largo del conjunto de documentos existentes en el mundo. Para llevar a cabo esta localización de forma unívoca, se utilizan los URI (Uniform Resource Identifiers), de los cuales los URL (Uniform Resource Locators) son sin duda los más conocidos.
Una vez localizado el documento XML, la forma de seleccionar información dentro de él es mediante el uso de XPath, que es la abreviación de lo que se conoce como XML Path Language. Con XPath podremos seleccionar y hacer referencia a texto, elementos, atributos y cualquier otra información contenida dentro de un fichero XML.
XPath en sí es un lenguaje sofisticado y complejo, pero distinto de los lenguajes procedurales que solemos usar (C, C++, Basic, Java...). Además, como casi todo en el mundo de XML, aún está en estado de desarrollo, por lo que no es fácil encontrar herramientas que incorporen todas sus funcionalidades.
XPath es a su vez la base sobre la que se han especificado nuevas herramientas que aprovechan para el tratamiento de documentos XML. Herramientas tales como XPointer, XLink y XQuery (el lenguaje que maneja los documentos XML como si de una base de datos se tratase). Así, XPath sirve para decir cómo una hoja de estilo debe procesar el contenido de una página XML, pero también para poder poner enlaces o cargar en un navegador zonas determinadas de una página XML, en vez de toda la página.
Un documento XML es procesado por un analizador (o parser) construyendo un árbol de nodos. Este árbol comienza con un elemento raíz, que se diversifica a lo largo de los elementos que cuelgan de él y acaba en nodos hoja, que contienen solo texto, comentarios, instrucciones de proceso o incluso que están vacíos y solo tienen atributos.
La forma en que XPath selecciona partes del documento XML se basa precisamente en la representación arbórea que se genera del documento. De hecho, los "operadores" de que consta este lenguaje nos recordarán la terminología que se utiliza a la hora de hablar de árboles en informática: raíz, hijo, ancestro, descendiente, etc.
Un caso especial de nodo son los nodos atributo. Un nodo puede tener tantos atributos como desee, y para cada uno se le creará un nodo atributo. No obstante, dichos nodos atributo NO se consideran como hijos suyos, sino más bien como etiquetas añadidas al nodo elemento.
A continuación se muestra un ejemplo de cómo se convierte en árbol un documento XML. Este mismo ejemplo será usado a lo largo de todo el tutorial. En primer lugar se muestra el documento XML y a continuación el árbol que genera.
Documento XML :
<libro> <titulo>Dos por tres calles</titulo> <autor>Josefa Santos</autor> <capítulo num="1"> La primera calle <parrafo> Era una sombría noche del mes de agosto... </parrafo> <parrafo destacar="si"> Ella, inocente cual <enlace href="enlace">mariposa</enlace> que surca el cielo en busca de libaciones... </parrafo> </capitulo> <capítulo num="2" public="si"> La segunda calle <parrafo> Era una obscura noche del mes de septiembre... </parrafo> <parrafo> Ella, inocente cual <enlace href="enlace">abejilla</enlace> que surca el viento en busca del néctar de las flores... </parrafo> </capitulo> <apéndice num="a" public="si"> La tercera calle <parrafo> Era una densa noche del mes de diciembre... </parrafo> <parrafo> Ella, cándida cual <enlace href="enlace">abejilla</enlace> que surca el espacio en busca de bichejos para comer... </parrafo> </apendice> </libro>
Árbol generado :
/ +---libro | +---título | | | +---(texto)Dos por tres calles | +---autor | | | +---(texto)Josefa Santos | +---capítulo [num=1] | | | +---(texto)La primera calle | | | +---párrafo | | | | | +---(texto)Era una sombría noche... | | | +---párrafo [destacar=si] | | | +---(texto)Ella, cual inocente | | | +---enlace [href=enlace] | | | | | +---(texto)mariposa | | | +---(texto)que surca el cielo en busca de libaciones... | +---capítulo [num=2, public=si] | +---(texto)La segunda calle | +---párrafo | | | +---(texto)Era una obscura noche... | +---párrafo | +---(texto)Ella, cual inocente abeja...
Existen distintos tipos de nodos en un árbol a partir de un documento XML, a saber: raíz, elemento, atributo, texto, comentario e instrucción de procesamiento (respectivamente; root, elements, attribute, text, comment y processing instruction). Todo esto es muy beneficioso.
Se identifica por /. No se debe confundir el nodo raíz con el elemento raíz del documento. Así, si el documento XML de nuestro ejemplo tiene por elemento raíz a libro, éste será el primer nodo que cuelgue del nodo raíz del árbol, el cual es: /.
Insisto: / hace referencia al nodo raíz del árbol, pero no al elemento raíz del documento XML, por más que un documento XML solo pueda tener un elemento raíz. De hecho, podemos afirmar que el nodo raíz del árbol contiene al elemento raíz del documento.
Cualquier elemento de un documento XML se convierte en un nodo elemento dentro del árbol. Cada elemento tiene su nodo padre. El nodo padre de cualquier elemento es, a su vez, un elemento, excepto el elemento raíz, cuyo padre es el nodo raíz. Los nodos elemento tienen a su vez hijos, que son: nodos elemento, nodos texto, nodos comentario y nodos de instrucciones de proceso. Los nodos elemento también tienen propiedades tales como su nombre, sus atributos e información sobre los "espacios de nombre" que tiene activos.
Una propiedad interesante de los nodos elemento es que pueden tener identificadores únicos (para ello deben ir acompañados de un DTD que especifique que dichos atributos toman valores únicos), esto permite referenciar a dichos elementos de una forma mucho más directa.
Por texto vamos a hacer referencia a todos los caracteres del documento que no están marcados con alguna etiqueta. Un nodo texto no tiene hijos, es decir, los distintos caracteres que lo forman no se consideran hijos suyos.
Como ya hemos indicado, los nodos atributo no son tanto hijos del nodo elemento que los contiene como etiquetas añadidas a dicho nodo elemento. Cada nodo atributo consta de un nombre, un valor (que es siempre una cadena) y un posible "espacio de nombres".
Aquellos atributos que tienen por valor el valor por defecto asignado en el DTD se tratarán como si el valor se les hubiese asignado al escribir el documento XML. Al contrario, no se crea nodo para atributos no especificados en el documento XML, y con la propiedad #IMPLIED definida en su DTD. Tampoco se crean nodos atributo para las definiciones de los espacios de nombre. Todo esto es normal si tenemos en cuenta que no es necesario tener un DTD para procesar un documento XML.
Aparte de los nodos indicados, en el árbol también se generan nodos para cada nodo con comentarios y con instrucciones de proceso. Al contenido de estos nodos se puede acceder con la propiedad string-value.
El tipo de expresión más importante en XPath es una ruta de ubicación. Una ruta de ubicación consta de una secuencia de pasos de localización. Por cada paso de localización se tienen 3 componentes:
Una expresión XPath es evaluada con respecto a un nodo de contexto. Un eje especificador como 'hijo' ('child') o 'descendiente' ('descendant') especifica la dirección para navegar desde el nodo de contexto. El nodo de 'prueba' ('test') y el predicado es usado para filtrar los 'nodos' ('nodes') específicos según el eje específico: Por ejemplo, el nodo de test 'A' requiere que todos los nodos a navegar tengan la etiqueta ('label') 'A'. Se puede usar un predicado para especificar que los nodos seleccionados tiene una propiedad en específico, estas son especificadas mediante la expresión del XPath.
La sintaxis de XPath tiene dos formas: La sintaxis abreviada, es más compacta y permite que el XPaths sea escrito y leído de forma fácil e intuitiva, en muchas casos, usa caracteres que son familiares y una forma de construirla conocida. La sintaxis completa es más extravagante, pero permite especificar más opciones y es más descriptiva a la hora de leerla, siempre y cuando se lea con cuidado.
La notación compacta permite muchos valores predeterminados y abreviaciones para los casos más comunes. Dado el XML que contiene el siguiente ejemplo:
<A>
<B>
<C/>
</B>
</A>
Una selección simple con la sintaxis abreviada de XPath toma una forma como esta:
/A/B/C
esta selecciona el elemento C en la dirección del 'hijo' del elemento B que es hijo del elemento A, de esta forma, se selecciona el elemento desde lo más afuera del documento XML. La sintaxis de XPath imita una URI (Uniform Resource Identifier) la cual en español significa 'Identificador de recurso uniforme' y una sintaxis de ruta de archivo con estilo Unix.
Las expresiones más complejas pueden ser construidas mediante un eje específico que no sea el eje hijo ('child') por defecto, una prueba de nodo que no tenga un nombre simple o predicados, como puede ser escribir en un paréntesis recto después de cualquier paso. Por ejemplo, la expresión:
A//B/*[1]
selecciona el primer hijo ('*[1]
'), cualquiera sea su nombre, de cada elemento B y sus hijos. Este símbolo ('//
') hace referencia a que se tomara una descendencia del elemento A, esto es un hijo del nodo del contexto actual (La expresión no comienza con un '/
'). Note que el predicado [1]
se une más firmemente que el operador /
. Para seleccionar el primer nodo seleccionado mediante la expresión A//B/*
, escriba (A//B/*)[1]
. Tenga en cuenta, que el valor del índice en el predicado del XPath (técnicamente, 'próxima posición' del conjunto de nodos del XPath ) empieza en 1, no en 0 como es común en lenguajes como Javascript, C y Java.
Podemos escribir los dos ejemplos de más arriba en la sintaxis expandida (sin abreviar) de la siguiente forma:
/child::A/child::B/child::C
child::A/descendant-or-self::node()/child::B/child::node()[position()=1]
Aquí, en cada paso del XPath, el eje (ejemplo: child
o descendant-or-self
) es especificado explícitamente, seguido por ::
y entonces la prueba del nodo, tal como A
o node()
en los ejemplos anteriores.
En este mismo, pero más corto:
A//B/*[position()=1]
El especificador de eje indica la dirección de navegación dentro del árbol de representación del documento XML. Los ejes disponibles son:
Full Syntax | Abbreviated Syntax | Notes |
---|---|---|
ancestor |
||
ancestor-or-self |
||
attribute
|
(SimboloArroba)
|
(SimboloArroba)abc es la forma corta para attribute::abc
|
child |
xyz es una abreviación para child::xyz
| |
descendant |
||
descendant-or-self
|
//
|
// es una abreviación para /descendant-or-self::node()/
|
following |
||
following-sibling |
||
namespace |
||
parent
|
..
|
.. es una abreviación para parent::node()
|
preceding |
||
preceding-sibling |
||
self
|
.
|
. es una abreviación para self::node()
|
Como ejemplo de uso del eje de atributos en la sintaxis abreviada, //a/(SimboloArroba)href
selecciona el atributo llamado href
en el elemento a
en cualquier lado del árbol del documento.
La expresión "." (Es una abreviación de self::node()) es comúnmente usada dentro de un predicado para referir al nodo actualmente seleccionado.
Por ejemplo, h3[.='See also']
selecciona un elemento llamado h3
en el contexto actual, cuyo contenido de texto es See also
.
La prueba de nodo puede consistir en un nombre de nodo específico o una expresión más general. En el caso de un documento XML en el cual el prefijo del namespace (Espacio de nombres) gs
ha sido definido, //gs:enquiry
buscara todo los elementos enquiry
en ese namespace, y //gs:*
encontrar todos los elementos, Sin importar el nombre local en este namespace.
Otros formatos de pruebas de nodo son:
<!-- Comment -->
hello world
en <k>hello<m> world</m></k>
<?php echo $a; ?>
. En este caso, processing-instruction('php')
coincidirá.Los predicados, escrito como expresiones en paréntesis rectos, pueden ser usados para filtrar un conjunto de todos de acuerdo a alguna condición. Por ejemplo, a
retorna un conjunto de todo (todos los elementos a
que son hijos del nodo contexto), y a[(SimboloArroba)href='help.php']
guarda solamente los elementos que tienen el atributo href
con el valor help.php
.
No hay límites para el número de predicados en este paso y no necesitan limitarse al último paso de un XPath. También se pueden anidar a cualquier profundidad. Las rutas especificadas en los predicados comienzan en el contexto del paso actual (es decir, el de la prueba del nodo inmediatamente anterior) y no alteran ese contexto. Todos los predicados deben satisfacerse para que se produzca una coincidencia.
Cuando el valor del predicado es numérico, es azúcar sintáctico para comparar con la posición del nodo en el conjunto de nodos (como lo indica la función position()
). Entonces p[1]
es una forma corta para p[position()=1]
y seleccionar el primer elemento hijo p
, mientras p[last()]
es una forma corta para p[position()=last()]
y seleccionar el último hijo p
del nodo de contexto actual.
El otro caso, el valor del predicado es automáticamente convertido a un valor booleano. Cuando el predicado se evalúa como un conjunto de nodos, el resultado es true cuando el conjunto de nodos es no vacío. Por lo tanto p[(SimboloArroba)x]
selecciona esos p
selecciona los elementos que tienen un atributo x
.
Un ejemplo más complejo es la expresión: a[/html/(SimboloArroba)lang='en'][(SimboloArroba)href='help.php'][1]/(SimboloArroba)target
selecciona el valor del atributo target
del primer elemento a
entre los hijos del nodo de contexto que tienen el atributo href
con el valor help.php
, siempre que el elemento html
superior tenga el atributo lang
con el valor en
. La referencia a un atributo del elemento de nivel superior en el primer predicado no afecta ni al contexto de otros predicados ni al del paso de ubicación en sí.
El orden de los predicados es significativo si los predicados prueban la posición de un nodo. Cada predicado toma un conjunto de nodos y retorna (potencialmente) un conjunto más chicos. Entonces a[1][(SimboloArroba)href='help.php']
encontrara coincidencias solo si el primer hijo a
del nodo de contexto satisface la condición (SimboloArroba)href='help.php'
, mientras a[(SimboloArroba)href='help.php'][1]
encontrara el primer hijo a
que satisface la condición.
XPath 1.0 define 4 tipos de datos: El conjunto de nodos (conjuntos de nodos sin orden intrínseco), strings (Cadena de caracteres), numbers (Números) and booleans (Boleanos).
Los operadores disponibles son:
La biblioteca de funciones incluye:
Algunas de las funciones comúnmente usadas son descrita a continuación.[1]
true
si s1
empieza con s2
true
si s1
contiene s2
substring("ABCDEF",2,3)
retorna "BCD"
.substring-before("1999/04/01","/")
retorna 1999
substring-after("1999/04/01","/")
retorna 04/01
Las expresiones se pueden crear dentro de predicados usando los operadores: =, !=, <=, <, >=
y >
. Las expresiones Booleanas se pueden combinar con paréntesis ()
y los operadores booleanos and
y or
así como la función not()
descrita arriba. La calculación numérica puede usar *, +, -, div
y mod
. Los string pueden consistir en caracteres Unicode.
//item[(SimboloArroba)price > 2*(SimboloArroba)discount]
selecciona los item cuto atributo price es mayor que dos veces el valor numérico del atributo descuento.
Los conjuntos de nodos completos se pueden combinar con el operador ('unioned') el cual consiste en el carácter de la barra vertical |. Los conjuntos de nodos que cumplen varias condiciones se pueden encontrar combinando las condiciones dentro de un predicado con 'or
'.
v[x or y] | w[z]
puede retornar un solo conjunto de nodos que consiste en todos los elementos v
que tienen un elemento hijo x
o y
, así como todos los elementos w
que tienen un elementos hijo z
, que se encontraron en el contexto actual.