VALIDACIÓN. LAS DTD.

Como hemos visto hasta ahora, las reglas de XML son bastante directas. No lleva demasiado tiempo crear un documento XML bien formado, que describan cualquier información. Cuando se crean documentos XML, se pueden clasificar grupos de documentos simlares basándonos únicamente en los elementos y atributos que contienen. De hecho, puede ser suficiente en un gran número de casos.

Ahora supongamos que estamos desarrollando una aplicación que usa el ejemplo <persona> que vimos anteriormente. Supongamos también que cierto usuario de nuestra aplicación coloca información que no se ajusta con el vocabulario que hemos desarrollado. ¿Cómo comprobar que el documento es válido? Podríamos desarrollar una aplicación que comprobase que el vocabulario es el correcto, y que está utilizado en el orden adecuado. Pero entonces si modificamos el tipo de documento tendríamos que volver a hacer un nuevo programa que verificase el nuevo tipo de documento. Crear una aplicación específica es muy poco eficiente.

La necesidad de validación es tan común en los lenguajes de marcas, que los diseñadores de XML incluyeron un método para comprobar la validez de un documento XML. Un documento XML es válido si su contenido coincide con su definición de elementos, atributos y otras partes del documento permitidos.

Usando DTDs (Document Type Definitions) se puede comprobar si el contenido de un documento es válido empleando para ello analizadores sintánticos especiales. La recomendación XML divide los analizadores sintácticos en dos categorías: Los analizadores con validación y sin validación. Solo un analizador con validación puede comprobar si el documento XML está escrito de acuerdo a las especificaciones de un DTD.

PREPARAR EL TERRENO

Para empezar vamos a buscarnos un programa que pueda validar XML. Existen varias alternativas. Nosotros nos hemos decantado por XML Copy Editor, entre otras cosas por ser multiplataforma. En cualquier caso hay muchas otras alternativas.

En GNU/Linux se puede usar XML Copy Editor, pero también se puede usar un párser clásico de IBM desde línea de consola. Si alguien está interesado, os muestro unas instrucciones sacadas de http://www.programacion.com/articulo/curso_de_xml_164/7

Para utilizar el parser de IBM, podemos descargar el siguiente archivo: http://www.programacion.com/cursos_descargas/joa_xml/down/parserxml.zip.

El fichero contiene los dos ficheros .jar que realmente hacen falta para utilizar el parser.

xml4j_1_1_14.jar, que contiene el parser propiamente dicho.

xml4jSamples_1_1_14.jar, que contiene unos ejemplos de como utilizarlo.

Una vez que tenemos los jar, y declarados en nuestro classpath para validar un documento XML, no tendremos más que escribir:

java samples.XJParse.XJParse -d [nombreficheroXML] >tmp.txt

Si tenemos instalado el JRE, lo mejor será colocar los .jar directamente en el directorio lib/ext, dentro del directorio donde esté instalado el JRE, puesto que Java busca en dicho directorio por defecto los .jar que pueda necesitar. En mi caso, el directorio mencionado está en /usr/lib/jvm/java-6-openjdk/jre/lib/ext/.

NOTA: para instalar JRE en Linux, tenemos el siguiente enlace: http://www.java.com/en/download/linux_manual.jsp

Una vez copiados los archivos .jar al directorio mencionado, entonces podemos ejecutar el parser con el comando siguiente:

java samples.XJParse.XJParse -d [nombreficheroXML] >tmp.txt

De momento dejamos esto aparcado, ya que antes de usar el validador, debemos saber crear un DTD.

En Internet podemos usar el validador online de la página http://www.xmlvalidation.com/

LAS DTD

Supongamos el siguiente ejemplo:

<?xml version="1.0"?> <!DOCTYPE mihtml [ <!ELEMENT mihtml (c1 | parrafo)*> <!ELEMENT c1 (#PCDATA)> <!ELEMENT parrafo (#PCDATA|negrita)*> <!ELEMENT negrita (#PCDATA)> ]> <mihtml> <c2> Texto c1 </c2> <parrafo> Parrafo con <negrita> texto en negrita </negrita> y <cursiva> cursivas </cursiva> </parrafo> </mihtml>

Vamos a validarlo. Cópialo y guárdalo con el nombre mihtml.xml.

Deberías obtener diversos errores. Aunque la sintaxis XML está bien formada, el documento no está de acuerdo con el DTD especificado. Nos indicará errores relacionados con elementos no válidos (en concreto c2 y cursiva) y con elementos que no siguen las reglas del DTD (en concreto "parrafo").

DESGLOSANDO EL DTD

Lo primero es declarar el documento XML.

<?xml version="1.0"?>

Lo siguiente, es la declaración del DTD, comunmente referido como "el DOCTYPE":

<!DOCTYPE mihtml [

A continuación declaramos los elementos del documento. La recomendación XML indica que los elementos declarados deben empezar con "!". La declaración de elementos debe aparecer solamente en la parte del DTD.

<!ELEMENT mihtml (c1 | parrafo)*> <!ELEMENT c1 (#PCDATA)> <!ELEMENT parrafo (#PCDATA|negrita)*> <!ELEMENT negrita (#PCDATA)>

NOTA: En este momento, ya hemos podido comprobar como la sintaxis de un DTD es muy diferente de las reglas básicas de los documentos XML. Los DTD fueron originalmente usados con SGML, y en XML se utiliza un subconjunto de los DTD de SGML para mantener la compatibilidad.

Finalmente se cierra la declaración del DTD con la cadena siguiente: "]>".

LAS DECLARACIONES DE TIPO DE DOCUMENTO (DTD)

Al abrir la declaración de tipo hay algunas cuestiones a tener claras. Como ya dijimos, XML es "case sensitive" (sensible a mayúsculas). Por ello, no es lo mismo escribir "doctype" que "Doctype" que "DOCTYPE". La forma correcta es DOCTYPE. Una vez abierta la declaración, hay que indicar el nombre el elemento raíz. En el caso anterior, el elemento raíz es "mihtml" y por eso la declaración del DTD empieza con "<!DOCTYPE mihtml". La declaración completa tiene la siguiente forma: <!DOCTYPE elmentoraiz [ ...Elementos... ]>

Las declaraciones que aparecen entre [ y ] son llamadas "subconjunto de declaraciones internas". Este nombre se debe a que también se pueden ubicar estas declaraciones en otro fichero independiente. En tal caso serían llamadas "subconjunto de declaraciones externas". Nos podemos referir a un DTD externo de dos maneras: identificadores de sistema, e identificadores públicos. Entraremos en este tema más adelante.

ANATOMÍA DE UN DTD

Las declaraciones de un DTD contienen tres partes básicas:

  • Declaraciones de elementos
  • Declaraciones de atributos
  • Declaraciones de entidades

DECLARACIONES DE ELEMENTOS

Observa la siguiente declaración:

<!ELEMENT persona (nombre, apellido1, apellido2)>

La declaración de un elemento consiste en tres partes básicas: La declaración ELEMENT, el nombre del elemento y el modelo de contenido del elemento. Estas tres partes en el ejemplo anterior son:

  • Declaración ELEMENT: <! ELEMENT ... >
  • Nombre del elemento: persona
  • Modelo de contenido del elemento: (nombre, apellido1, apellido2)

Un detalle importante, es que el nombre del elemento debe aparecer exactamente igual a como aparece en el documento XML, incluyendo cualquier prefijo de namespace. Este hecho es una limitación. Esencialmente esto significa que los usuarios no pueden elegir sus propios prefijos para los namespaces, sino que deben usar los prefijos definidos en el DTD. Esta limitación existe debido a que la W3C completó la recomendación XML antes de terminar de definir como funcionarían los namespaces. En secciones posteriores, veremos como los documentos de esquemas XML y RELAX NG (que son otros mecanismos para definir un documento) no están limitados de esta forma.

El modelo de contenido del elemento define el contenido permitido dentro del elemento. Un elemento puede contener elementos hijos, texto, una combinación ambos, o incluso estar vacío. Este es exactamente el meollo del DTD, donde se define la estructura completa del documento. Existen cuatro tipos de modelo de contenido: Elemento, Mixto, Vacío o Any.

MODELO DE CONTENIDO "ELEMENTO"

Muchos elementos en XML contienen otros elementos. Cuando se define un modelo de contenido "elemento", simplemente se incluyen los elementos aceptables entre paréntesis. Por ejemplo, si tenemos un elemento "contacto" al que solo se le permite contener un elemento "nombre", la declaración puede ser como la siguiente:

<!ELEMENT contacto (nombre)>

En una lista de contactos, sin embargo, lo normal es que el elemento "contacto" necesite incluir más cosas que un simple nombre, como por ejemplo una localización, un teléfono y una descripción. En tal caso podríamos hacer una declaración como la que sigue:

<!ELEMENT contacto (nombre, localización, teléfono, descripción)>

Cada elemento especificado en el modelo de contenido de "contacto" deberá tener su propia definición en el DTD. Es decir, que en el ejemplo anterior habrá que incluir declaraciones ELEMENT para "nombre", "localización", "teléfono" y "descripción". Por esta razón, no se pueden repetir los nombres de los elementos en las definicinones del DTD, ya que habrían definiciones ELEMENT repetidas.

Las declaraciones se pueden realizar en cualquier orden, ya que el analizador sintático que usemos para la validación sabe como manejar estas definiciones. Como ya hemos dicho, en la declaración ELEMENT, los elementos deben aparecer tal como lo hará en el documento XML, incluyendo prefijos de namespaces si fuese necesario.

En el ejemplo anterior, hemos visto como se definía una lista de nombres de elemento en modelo de contenido. En realidad se pueden expresar elementos de dos maneras principales: mediante listas, y mediante opciones.

Las listas ya las hemos visto con el ejemplo anterior (nombre, localización...). Si en el documento falta alguno de los elementos de la lista dentro del elemento "contacto", el analizador sintáctico dará un error durante la validación.

Las opciones permiten elegir entre unos elementos u otros, pero no ambos. Por ejemplo, consideremos que el elemento "localización" puede ser expresado mediante una dirección o mediante coordenadas GPS. En tal caso, la declaración sería del siguiente modo:

<!ELEMENT localización (dirección | GPS)>

Si el elemento "localización" apareciese vacío, o tuviese más de uno de estos elementos, el analizador sintático devolvería un error durante la validación.

Se pueden combinar listas y opciones. Por ejemplo, observa el siguiente ejemplo:

<!ELEMENT localización (dirección | (latitud, longitud))>

En este caso, el elemento "localización" contendrá, o bien un solo elemento "dirección", o bien los elementos "latitud" y "longitud". Es importante observar como se anidan los paréntesis. Este proceso de combinación se puede repetir tantas veces como fuese necesario.

MODELO DE CONTENIDO "MIXTO"

La recomendación XML especifica que cualquier elemento con un texto en su contenido es un modelo de contenido mixto. En los modelos de contenido mixto, el texto aparece solo o intercalado entre elementos. Observa la siguiente declaración:

<!ELEMENT nombre (#PCDATA)>

La palabra reservada PCDATA indica que los datos de tipo "carácter" en el modelo de contenido deberían ser analizados sintácticamente por el analizador. El siguiente elemento se ajusta a la declaración anterior:

<nombre> José </nombre>

Los modelos de contenido mixto pueden tambien contener elementos intercalados dentro del texto. Supongamos que queremos incluir una descripción de cada contacto en nuestro documento XML. Podríamos crear un nuevo elemento "descripción" que nos permita especificar donde hay un salto de línea (br), e indicar cuando el texto puede ser enfatizado (em) o puesto en negrita (strong):

<descripción> Juan es autor de una novela <em> de ciencia ficción </em>. <br/> A Juan <strong>le encanta escribir</strong> </descripción>

Para declarar este modelo de contenido mixto en el DTD, debemos usar el mecanismo de elección. Esto significa que cada elemento del contenido debe estar separado por "|". Por ejemplo:

<!ELEMENT descripción (#PCDATA | em | strong | br)*>

Cuando se utiliza un modelo de contenido mixto, la palabra #PCDATA debe aparecer siempre primero en la lista de elecciones. Esto permite al analizador sintáctico entender que se trata de un modelo de contenido mixto cuando encuentra #PCDATA al principio durante la validación.

En la declaración anterior se observa la aparición del símbolo *. Se trata de un indicador de cardinalidad. Existen otros indicadores de cardinalidad, como + y ?, que veremos más adelante. En concreto, * indica que no hay control sobre el orden o número de elementos dentro del contenido mixto. Podemos tener un número ilimitado de elementos <strong>, elementos <em>, elementos br y cualquier cantidad de texto.

MODELO DE CONTENIDO "VACÍO"

Observa el siguiente ejemplo de XML:

<br/>

Se trata de un elemento vacío. No tiene sentido introducir texto en una etiqueta como br. Su declaración sería como la siguiente:

<!ELEMENT br EMPTY>

Solo debemos declara un elemento vacío si realmente va a estar siempre vacío. En otro caso no debemos declararlo como vacío.

NOTA: En cualquier caso, un elemento no declarado como vacío puede estar vacío si se declara correctamente.

MODELO DE CONTENIDO "ANY"

Finalmente se puede declarar un elemento usando la palabra reservada ANY, que permite ser menos restrictivo sobre el modelo de contenido. Por ejemplo:

<!ELEMENT descripción ANY>

En el ejemplo anterior, la palabra ANY indica que cualquier elemento declarado en el DTD puede ser usado dentro del contenido de "descripción". En realidad, los DTD se utilizan para restringir el contenido de los documentos, por lo que ANY no es muy práctico.

Práctica 1. Observa el siguiente documento XML e induce un DTD a partir de él. Crea la declaraciones que necesites y luego comprueba que puedes validarlo.

<?xml version="1.0"?> <!DOCTYPE contactos [ ...Declaraciones de elementos aquí... ]> <contactos> <contacto> <persona> <nombre>José</nombre> <apellido1>Pérez</apellido1> <apellido2>García</apellido2> </persona> <localización> <latitud>34.031892</latitud> <longitud>-117.207642</longitud> </localización> <teléfono>953-232-731</teléfono> <conocidos>David Montero, Francisco Maya</conocidos> <descripción>José es el autor de una novela <em>de ciencia ficción</em>.<br/>A José <strong>le encanta</strong> la escritura. </descripción> </contacto> </contactos>

Entrega el ejercicio con el nombre Act1-dtd.xml

Saludos.