jueves, 24 de julio de 2008

Java y XML: DOM (I)

Siguiendo al hilo del post Java y XML, ahora toca centrar la atención en DOM (Document Object Model). Lo cierto es que la traducción es un poco complicada, pero viene a decir que se trata de un Modelo de Objetos para representación de Documentos. Dicho de otra forma, DOM genera un árbol de objetos (Nodos) que representan al documento XML. Cada uno de estos nodos pueden ser de muchos tipos: documento, elemento, atributo, comentario,... y todos se relacionan entre sí mediante relaciones padre-hijo.

DOM no es sólo un parseador como lo era SAX, sino que también permite modificar los documentos parseados, así como crear documentos XML totalmente nuevos. Desde mi punto de vista, al haber un árbol de objetos relacionados entre sí, la programación con DOM es más intuitiva para el desarrollador.

Realmente DOM es una especificación de la W3C, por lo que su objetivo principal es proporcionar una interfaz estándar que pueda ser utilizada en una amplia variedad de entornos y lenguajes de programación (Java, Javascript, C++, PHP, Python, Ruby,...).

Esto tiene su parte positiva, ya que es independiente del lenguaje de programación, pero también su parte negativa, ya que muchas clases son interfaces y esto implica que su uso sea un poco más laborioso. Ya veremos como APIs específicamente diseñadas para Java como JDOM y XOM, que aunque se alejan un poco de la especificación, desde el punto de vista de la programación en Java son mucho más sencillas precisamente por sustituir estas interfaces por clases directamente. El eterno dilema entre generalidad y eficiencia.

En este ejemplo utilizaremos las implementaciones DOM para Java JAXP (Java Api for XML Processing, desarrollada por Sun) y Xerces (desarrollada por Apache). Ojo, no confundir estas implementaciones con JDOM y XOM. JDOM y XOM no son implementaciones de DOM en sentido estricto ya que no cumplen las especificaciones de la W3C, aunque sí que están basadas en DOM.

Antes de pasar a la práctica comentar que se han definido 3 niveles de la especificación DOM:
  • Nivel 1 (1998). Consta de dos partes: el núcleo DOM, que incluye la funcionalidad para representar documentos XML; y DOM HTML.
  • Nivel 2 (2000). Mejora aspectos del nivel 1 e incluye: control de presentación mediante CSS, control de eventos, proporciona soporte para los espacios de nombres, puede crearse documentos DOM desde cero, importar nodos de otros documentos DOM, ...
  • Nivel 3 (2004). Incluye validación de documentos mediante DTD y XMLSchema, normalización de documentos, ...
Bien. Dicho esto ya podemos pasar a un tema más práctico, que es lo que realmente nos gusta. Aunque lo realmente práctico (ejemplos) lo dejaremos para el siguiente post.

Hemos dicho que DOM generaba un árbol de nodos a partir de un documento XML. Esto lo que quiere decir en palabras más mundanas es que lee el documento XML (fichero si quieres), lo procesa y genera una serie de objetos que representan cada una de los elementos del documento XML. Por supuesto, estos elementos están relacionados entre sí. Por ejemplo, las etiquetas están relacionadas con sus atributos y sus etiquetas hijas.

Vamos a ver qué tipos de nodos existen en DOM o al menos los más utilizados. Todas estas interfaces se encuentran en el paquete org.w3c.dom y todas heredan de la interfaz Node:

- Document
: representa al documento XML en sí. Es el objeto principal del árbol y sólo puede haber uno por documento XML. El resto de objetos del árbol deben pertenecer al ámbito de este objeto para poder estar en el árbol. Esto quiere decir que todos los objetos que vayan a ser insertados en el árbol deben ser creados o importados por el Document antes de ser insertado como hijo de cualquier elemento.

Almacena información como el encoding (getEncoding), la versión xml (getXmlVersion), si el documento es Standalone (getStandalone), y por supuesto tiene como único hijo a la etiqueta raíz del documento (getDocumentElement).

- Element:
representa una etiqueta XML. Almacena entre otras la lista de atributos que posee (getAttributes), la lista de nodos hijos (getChildNodes), contenido de tipo texto (getTextContent). Tiene métodos de acceso directo a su primer hijo (getFirstChild) y acceso directo a su próximo nodo hermano (getNextSibling). Ambos devuelven un Node por lo que el primer hijo o el próximo hermano pueden ser de cualquier tipo (Element,Text, CDATASection,....).

- Attr: atributo dentro de un Element.

- CharacterData: texto dentro del XML. Puede ser del tipo CDATASection, Text o Comment. Estas 3 interfaces heredan de CharacterData.

DOM te permite interactuar con estos elementos del árbol permitiendo añadir, modificar, eliminar, copiar y mover nodos. Se puede partir de un documento XML parseado, proveniente de un fichero por ejemplo, o crear un documento XML completamente nuevo. En cualquier momento el arbol puede ser convertido a cadena de caracteres o bytes (serializar). En pocas palabras los pasos serían:

  1. Parsear un documento XML en formato String o crear un documento XML desde cero. Esto nos proporciona el objeto Document
  2. Modificar, añadir o eliminar elementos al Document y sus hijos.
  3. Serializar el árbol XML en formato String o en un fichero para su almacenaje.
Por último comentar que no todo es bueno o malo. Igual que decíamos cosas malas de SAX, de DOM hay que decir que ese árbol de nodos está en memoria (RAM) y por tanto está ocupando un espacio que puede ser considerable dependiendo del tamaño del XML. En principio no hay por qué alarmarse ya que hoy en día la RAM no es un problema para los ordenadores. Sin embargo, tampoco hay que olvidar del todo esto ya que si se descuida en el momento más inesperado pueden producirse los famosos OutOfMermoryException. Y según la ley de Murphy esto ocurrirá en el peor momento de todos, es decir, cuando el sistema está en producción.

Teniendo claro estos conceptos, ya podemos pasar a los ejemplos, aunque esto será después de la publicidad, .... digo en el próximo post. Supongo que estará listo en un par de días como muy tarde ;-)

3 comentarios:

Fran dijo...

Hola Xela, he tratado de poner este comentario en la parte de JDOM pero no me ha dejado. Igual es una pregunta un poco chorra, pero ahi va:

Tengo un trozo de codigo en mi programa tal como:

Element condition = new Element("condition");
condition.setAttribute("type",rule.condition);
rulE.addContent(condition);

Con el que obtengo lo siguiente:

condition type="ERULcondition" /

El asunto es que entre el cierre de comillas y la barra / se me forma un espacio.
Hay alguna manera de evitar eso??

Muchas gracias!
Fran.

Xela dijo...

Fran, parece ser que el OutputFormat no permite indicar eso que comentas (o al menos no me ha parecido verlo). Lo único que se me ocurre es que te implementes tu propio XMLSerializer. Bastaría con heredar del original y sobreescribir el método startElement para hacer que no meta ese espacio que te molesta. ;-)

Jorge Aparicio dijo...

Felicidades por este estupendo post