Anexo: Ejercicios de XPath¶
En los ejercicios siguientes se asume que se va a utilizar el fichero siguiente:
<inventario>
<producto codigo="AAA-111">
<nombre>Teclado</nombre>
<peso unidad="g">480</peso>
</producto>
<producto codigo="ACD-981">
<nombre>Monitor</nombre>
<peso unidad="kg">1.8</peso>
</producto>
<producto codigo="DEZ-138">
<nombre>Raton</nombre>
<peso unidad="g">50</peso>
</producto>
</inventario>
Resolver los siguientes problemas usando expresiones XPath. Si no nos dicen nada se puede asumir que las etiquetas no deben incluirse.
Extraer todos los elementos peso (etiqueta incluida).
Extraer las cantidades de todos los elementos peso (sin la etiqueta <peso>).
Extraer el peso del ultimo elemento.
Extraer las distintas unidades en las que se han almacenado los pesos.
Extraer el penúltimo codigo.
Extraer el peso del elemento cuyo codigo sea AAA-111.
Extraer el nombre de los productos que hayan puesto el peso en gramos.
Extraer el codigo de los productos cuyo nombre sea «Monitor».
Extraer el código de los productos que pesen más de un cuarto de kilo.
Soluciones¶
Para el enunciado Extraer todos los elementos peso (etiqueta incluida) . La solución sería algo como esto /inventario/producto/peso
. De hecho nos devuelve esto:
<peso unidad="g">480</peso>
<peso unidad="kg">1.8</peso>
<peso unidad="g">50</peso>
Enunciado: Extraer las cantidades de todos los elementos peso (sin la etiqueta <peso>). La solución sería /inventario/producto/peso/text()
. La expresión devuelve
480
1.8
50
Enunciado:Extraer el peso del ultimo producto.. Una posible solución equivocada sería esta /inventario/producto/peso[last()]
, que en realidad recupera «el último peso de cada producto», es decir recupera muchos, como muestra el resultado siguiente:
<peso unidad="g">480</peso>
<peso unidad="kg">1.8</peso>
<peso unidad="g">50</peso>
La expresión correcta sería /inventario/producto[last()]/peso
que devuelve esto
<peso unidad="g">50</peso>
Enunciado: Extraer las distintas unidades en las que se han almacenado los pesos. Una posible solución sería esta /inventario/producto/peso/@unidad
, que devuelve esto:
g
kg
g
Enunciado: Extraer el penúltimo codigo.. Una posible solucion sería esta:/inventario/producto[last()-1]/@codigo
Enunciado: Extraer el peso del elemento cuyo codigo sea AAA-111. /inventario/producto[@codigo="AAA-111"]/peso
Esto devuelve como resultado:
<peso unidad="g">480</peso>
Enunciado: Extraer el nombre de los productos que hayan puesto el peso en gramos.
Una idea incorrecta sería esta /inventario/producto/peso[@unidad="g"]
.
Esta está mal, porque recupera «pesos» en lugar de «nombres» . De hecho recupera esto:
<peso unidad="g">480</peso>
<peso unidad="g">50</peso>
Una correcta sería
/inventario/producto[peso/@unidad="g"]/nombre
Y otra posibilidad sería
/inventario/producto[peso[@unidad="g"]]/nombre
Que literalmente pone «extraer el nombre de productos que cumplan la condicion de tener un hijo peso y a su vez ese hijo peso cumpla la condición de tener un atributo unidad con el valor g»
Esto devuelve
<nombre>Teclado</nombre>
<nombre>Raton</nombre>
Enunciado: Extraer el codigo de los productos cuyo nombre sea «Monitor»
Una posible solución sería
/inventario/producto[nombre/text()="Monitor"]/@codigo
Aunque en realidad también serviría lo siguiente:
/inventario/producto[nombre="Monitor"]/@codigo
La clave es que como el elemento nombre no tiene hijos entonces se permite comparar el elemento como una cadena. El evaluador XPath sobreentiende que queremos comparar «el contenido del elemento nombre» con la cadena «Monitor».
Enunciado: Extraer el código de los productos que pesen más de un cuarto de kilo..
La solución sería
/inventario/producto[
(peso/@unidad="g" and peso/text()>"250")
or
(peso/@unidad="kg" and peso/text()>"0.25")
]/@codigo
Información bancaria¶
Dado el siguiente fichero XML, contestar a las preguntas que se enuncian más abajo usando XPath.
<listado>
<cuenta>
<titular dni="5671001D">Ramon Perez</titular>
<saldoactual moneda="euros">12000</saldoactual>
<fechacreacion>13-abril-2012</fechacreacion>
</cuenta>
<fondo>
<cuentaasociada>20-A</cuentaasociada>
<datos>
<cantidaddepositada>20000</cantidaddepositada>
<moneda>Euros</moneda>
</datos>
</fondo>
<fondo>
<cuentaasociada>21-DX</cuentaasociada>
<datos>
<cantidaddepositada>4800</cantidaddepositada>
<moneda>Dolares</moneda>
</datos>
</fondo>
<cuenta>
<titular dni="39812341C">Carmen Diaz</titular>
<saldoactual moneda="euros">1900</saldoactual>
<fechacreacion>15-febrero-2011</fechacreacion>
</cuenta>
</listado>
Consulta: «cantidad depositada»¶
Extraer la cantidad depositada en la cuenta 20-A
/listado/fondo
nos devolvería todos los elementosfondo
/listado/fondo/datos
nos devolvería todos los elementosdatos
que sean hijos defondo
los cuales a su vez deben ir dentro deinventario
.
Como nos piden una cantidad el último elemento XPath debe ser forzosamente cantidad
. Como nos ponen una condición tendremos que usar corchetes. El elemento cuentaasociada
es hijo de fondo
así que una buena posibilidad sería poner la condicion con el elemento fondo, así tendríamos que
Si añadimos la condición parece entonces que una buena posibilidad sería algo como esto:
/listado/fondo[cuentaasociada = "20-A"]/datos/cantidaddepositada
Si realmenten nos piden la cantidad sin etiquetas podemos añadir al final /text()
y dejarlo así:
/listado/fondo[cuentaasociada = "20-A"]/datos/cantidaddepositada/text()
Consulta: «monedas usadas»¶
Extraer un listado sin etiquetas de todas las monedas usadas por los distintos fondos
La consulta sería simplemente:
/listado/fondo/datos/moneda/text()
Consulta: «dnis con dólares»¶
Extraer el DNI de las cuentas que usen dolares como moneda de base
En las cuentas la moneda es un atributo de saldoactual
, por lo que la condición debería ser:
...[@moneda="dolares"]...
Como nos piden el DNI este debe ser el campo que debe aparecer al final del XPath. Aparte de eso el DNI es un atributo de titular
, por lo que la consulta debería ser algo así:
...[@moneda="dolares"]... /@dni
Como el dni
es un atributo de titular
y moneda
es un atributo de saldoactual
una buena posibilidad es mover la condición al elemento padre cuenta
y hacer algo así:
/listado/cuenta[saldoactual/@moneda = "dolares"]/titular/@dni
Obsérvese que para el ejemplo dado no hay ningún resultado (de hecho el evaluador dará error porque nadie cumple la condición). Si se desea comprobar que el XPath está bien se invita al lector a añadir datos al conjunto para verificar que la expresión XPath funciona correctamente.
Consulta: «fondos con menos de 2500 euros»¶
Extraer toda la información de los fondos que usen «euros» por un valor inferior a 2500
Por el texto del enunciado es evidente que el último elemento del XPath debe ser fondo
, por lo que de momento podemos saber que la expresión tendría este aspecto:
.../fondo
También es evidente que hay una condición de filtrado. Esta condición tiene dos partes:
Por un lado la divisa debe ser «Dolares». La divisa va dentro del elemento
moneda
que es hijo dedatos
que a su vez es hijo defondo
Por otro lado la cantidad debe ser inferior a 2500. Esta información está en
cantidaddepositada
.Para este caso necesitamos que se cumplan ambas condiciones por lo que deberemos usar
AND
Una posible consulta que resuelve esto es:
/listado/fondo[datos/cantidaddepositada<2500 and datos/moneda = "Dolares"]
De nuevo nos encontramos con que el programa puede dar un error, lo que tiene todo el sentido del mundo, ya que le pedimos que extraiga datos de algo que no existe.
Ejercicios resueltos sobre «proveedores/partes»¶
XPath I¶
Recuperar el nombre de todas las partes, sin etiqueta, solo textos
Solución:
/datos/partes/parte/nombreparte/text()
//nombreparte/text()
XPath II¶
Recuperar el nombre de los proyectos con el numproyecto y1, y3 e y5.
Tiene trampa. Al leer deprisa podemos pensar que la condición es un and
- Solución::
//proyecto[@numproyecto=”y1” and @numproyecto=”y3” and @numproyecto=”y5”]/nombreproyecto
Pero nadie cumple esa condición. Nadie puede tener los tres código a la vez. En realidad la solución es con or
:
//proyecto[@numproyecto='y1' or @numproyecto='y3' or @numproyecto='y5']/nombreproyecto
XPath III¶
Recuperar el nombre de los proyectos donde la cantidad suministrada sea mayor que 550.
Tal y como está planteado no se puede resolver con XPath