XMLStarlet

Es un programa para la CLI de Linux que nos permite hacer validaciones, extracción de datos con XPath 1.0 y transformaciones con XSLT.

Bien formado

Para comprobar si un XML está bien formado basta con hacer:

$ xmlstarlet val -e claustro.xml

La opción -e provoca que se muestren los errores, en caso de que los hubiera. De lo contrario sólo informa de si es válido. Obsérvese que el mensaje no es preciso: debería indicar, simplemente, que es bien formado, ya sólo comprueba si el XML se ajusta a las reglas generales del XML.

Validación

Para realmente validar un documento, y no simplemente comprobar que está bien formado, el programa nos da la posibilidad de usar DTD, XSD o RNG.

DTD

Es necesario añadir la opción -d:

$ xmlstarlet val -e -d claustro.dtd claustro.xml

Pero si el DTD está declarado en el XML (en la declaración de tipo de documento), puede simplificarse a:

$ xmlstarlet val -e -E claustro.xml

XSD

Para validar un XML haciendo uso de un XSD, puede usarse la opción -sp:

$ xmlstarlet val -s archivo.xsd archivo.xml

Como en casos anteriores, si se quiere ver cuáles son los errores, hay que incluir la opción -e:

$ xmlstarlet val -s archivo.xsd -e archivo.xml

RNG/RNC

xmlstarlet no es capaz de validar directamente usando RNC, pero tenemos la opción de usar un conversor como trang, del que hay en Debian un paquete con ese mismo nombre, para transformar el RNC en un RNG:

$ trang casilleros.rnc casilleros.rng
$ xmlstarlet val -e -r casilleros.rng casilleros.xml  # -e muestra errores si los hay

Nota

trang traduce a DTD (o XSD), por lo que también podemos obtener el DTD (o XSD) equivalente a nuestro RNC:

$ trang casilleros.rnc casilleros.dtd

Puede ser útil esta traduccion para ver cómo se ve la gramática definida en DTD, pero es absolutamente desconsejable hacer esta traducción con el propósito de validar después, ya que DTD es menos expresivo y, en consecuencia, es muy probable que se pierda expresividad.

Nota

trang permite también otra cosa: obtener la gramática a partir del documento XML:

$ trang casilleros.xml casilleros.rnc  # O casilleros.dtd para obtener el DTD

Esto, sin embargo, crea siempre gramáticas deficientes, ya que rara vez la herramienta es capaz de adivinar nuestra intención de hacer que un atributo fuera un identificador o que un contenido responda a una enumeración. Su utlidad se reduce, por lo general, a brindarnos un esqueleto sobre el que perfilar la gramática.

XPath

Con este programa podemos usar XPath tanto para seleccionar datos como para introducir modificaciones al XML original. Para lo segundo es mejor XQuery o XSLT, pero por completar la información sobre la orden incluiremos las posibilidades que brinda.

Selección

Para lograrlo es necesario usar el comando sel, seguido de la opción -t, y la acción que se quiere realizar a continuación, que puede ser:

-v <expresión-xpath>

Muestra el valor de lo seleccionado mediante la expresión. Se debe usar esta opción cuando el resultado de la expresión es un valor escalar: un único atributo, un nodo que contiene texto o la evaluación de una función o una operación. Por ejemplo:

$ xmlstarlet sel -t -v '//profesor[1]/@id' -n casilleros.xml
p1

Nota

Se ha añadido la opción -n para añadir un cambio de línea al final del resultado.

-c <expresion-path>

Copia el nodo seleccionado, por tanto deberá usarse cuando el resultado de nuestra selección es un nodo elemento o un conjunto de nodos elemento. Por ejemplo, lo siguiente devuelve todos los nodos apelativo:

$ xmlstarlet sel -t -c '//profesor/apelativo' -n casilleros.xml
<apelativo>Pepe</apelativo><apelativo>Paco</apelativo><apelativo>Mari</apelativo>
-m <expresión-path>

Selecciona un nodo o un conjunto de nodos para seguir trabajando sobre ellos. Por tanto, requerirá que luego se use -v o -c. Por ejemplo:

$ xmlstarlet sel -t -m '//profesor/apelativo' -v '.' casilleros.xml
PepePacoMari

En este caso, habría sido fundamental usar -n:

$ xmlstarlet sel -t -m '//profesor/apelativo' -v '.' -n casilleros.xml
Pepe
Paco
Mari

Nota

xmlstarlet es capaz de devolvernos el código XSLT equivalente a nuestra selección añadiendo tras sel la opción -C.

Edición

En este caso, debe usarse ed, una acción y el uso de expresiones XPath:

-d <expresion-xpath>

Borra los nodos que selecciona la expresión:

$ xmlstarlet ed -d '//profesor[last()]' casilleros.xml

Esta expresión elimina el último de los profesores.

-u <expresion-xpath> -v valor

Actualiza el valor del nodo o nodos seleccionados:

$ xmlstarlet ed -u '//profesor[1]/apelativo' -v "Manolo" casilleros.xml
-i <expresión-xpath> -t (elem|text|attr) -n <nombre> -v <valor>

Permite insertar antes del nodo seleccionado (o los nodos) un nodo de tipo elemento, texto o atributo del nombre y valor referidos. Se deben hacer varias puntualizaciones:

  • Los nodos de texto no tienen nombre, así que da igual el nombre que se les dé.

  • El valor sólo puede ser texto, así que, directamente, sólo se pueden crear nodos elemento que contienen texto.

  • Si se quiere crear nodos elemento que tienen atributos o contienen otros nodos elemento, entonces hay que componer varias acciones.

Por ejemplo, esto añadiría un nuevo apelativo al primer profesor:

$ xmlstarlet ed -i '//profesor[1]/nombre' -t elem -n "apelativo" -v "Pancho" casilleros.xml
-a <expresión-xpath> -t (elem|text|attr) -n nombre -v valor

Como -i pero añade el nodo después, no antes. Por ejemplo:

$ xmlstarlet ed -a '//profesor[1]/apelativo[last()]' -t elem -n "apelativo" -v "Pancho" casilleros.xml
-s <expresión-xpath> -t (elemen|text|attr) -n nombre -v valor

Añade el nuevo elemento como último elemento del elemento seleccionado:

$ xmlstarlet ed -s '//profesor[2]/nombre' -t text -n foo -v " María" casilleros.xml

XSLT

XMLStarlet usa las librerías libxml2 y libxslt, de modo que sólo tiene soporte para XSLT 1.0 y bastantes extensiones. La manera más sencilla de usarlo es utilizar el argumento tr seguido de la hoja XSLT y el XML original:

$ xmlstarlet tr disco.txt.xsl disco.xml

Ahora bien, si el XML declara algún hoja XSLT para su transformación con la instrucción de procesamiento <?xml-steelsheet ... ?> (véanse los ejemplos ya expuestos), entonces puede omitirse el fichero xsl incluyendo el parámetro -E:

$ xmlstarlet tr -E disco.xml

En el ejemplo, la hoja de transformación debería llamarse discos.txt.xsl, de modo que esta última orden es equivalente a la anterior. En algunas hojas de transformación se requiere pasar parámetros. Para ello existen las opciones -p y -s. Ambas sirven para lo mismo con la diferencia de que -s está pensada para pasar parámetros que sean cadenas. Por ejemplo:

$ xmlstarlet tr libros.xsl -p "año=1994" -s "bibliotecario=Juan Palomo" \
                           -p "nombre='Biblioteca Municipal'" libros.xml

Obsérvese la diferencia: se han escrito todas las parejas nombre-valor entre comillas dobles para que bash no haga travesuras. La primera pareja es un número y las dos restantes son cadenas. Como con la segunda se usó -s, bastó con escribir tal cual la cadena. En la tercera, sin embargo, se escribió -p, así que hubo explícitamente que informar al procesador de que era una cadena escribiendo el valor entre comillas.