viernes, 25 de julio de 2008

Java y XML: DOM (II)

Este post complementa al post anterior exponiendo una serie de ejemplos de uso de DOM. Como no, este post pertenece a la serie de post dedicados a Java y XML.

Como ya comenté, utilizaremos las implementaciones DOM para Java JAXP (Java Api for XML Processing, desarrollada por Sun) y Xerces (desarrollada por Apache). La implementación JAXP viene con la librería básica de Java (vamos la que te descargas de la página oficial de Sun: SDK o JRE), tanto en la versión 1.4.2 como en las posteriores. Según he podido comprobar la implementación de DOM de Xerces también ha sido incluida en la librería básica de Java a partir de la versión 1.5.0. Por tanto si usas java 1.4.2 necesitarás incluir la librería de Xerces en tu aplicación. Por el contrario, con Java 1.5 o 1.6 no necesitarás librerías adicionales.

Vamos a partir del fichero XML siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<etiquetaPrincipal xmlns:xela="http://www.latascadexela.es" >
   <etiquetaHija id="1" atributo1="valorAtributo1" atributo2="valorAtributo2">
      Texto dentro de la etiqueta hija
   </etiquetaHija>
   <!-- Comentario -->
   <xela:etiquetaConNamespace descripcion="etiqueta con un namespace"/>   
</etiquetaPrincipal>

Parseando un XML

Vamos a ver como obtener el objeto Document del árbol de nodos partiendo de un fichero XML.

Con JAXP

// Construimos nuestro DocumentBuilder
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
// Procesamos el fichero XML y obtenemos nuestro objeto Document
Document doc = documentBuilder.parse(new InputSource(new FileInputStream("/ruta_a_fichero/fichero.xml")));

Con Xerces

// Creamos el parseador
DOMParser parser = new DOMParser();
// Procesamos el fichero XML
parser.parse(new InputSource(new FileInputStream("/ruta_a_fichero/fichero.xml")));
// Obtenemos el objeto Document
Document doc = parser.getDocument();

Recorriendo el árbol de nodos

Una vez tenemos el objeto Document podemos ir descendiendo por el árbol de nodos a partir de la etiqueta principal. Y así sucesivamente por los hijos de los nodos.

// Obtenemos la etiqueta raiz
Element elementRaiz = doc.getDocumentElement();
// Iteramos sobre sus hijos
NodeList hijos = elementRaiz.getChildNodes();
for(int i=0;i<hijos.getLength();i++){
   Node nodo = hijos.item(i);
   if (nodo instanceof Element){
      System.out.println(nodo.getNodeName());
   }
}

Como podéis ver no está de más asegurarse de que los nodos devueltos son Element.No todos los hijos de un Element lo serán. Puede evitar muchos errores el acostumbrase a ello, ya que son errores en tiempo de ejecución y el compilador no nos avisa de ellos.

Sin embargo, lo que nos interesa normalmente es obtener una serie de etiquetas a partir de su nombre. Es aquí donde podemos usar el método getElementsByTagName o getElementsByTagNameNS si buscamos etiquetas con espacios de nombres. Por cierto, getElementsByTagNameNS no me ha funcionado con JAXP, por lo que si usáis espacios de nombres os aconsejo Xerces.



// Buscamos una etiqueta dentro del XML
NodeList listaNodos = doc.getElementsByTagName("etiquetaHija");
for(int i=0;i<listaNodos.getLength();i++){
   Node nodo = listaNodos.item(i);
   if (nodo instanceof Element){
      System.out.println(nodo.getTextContent());
   }
}

// Buscamos una etiqueta dentro del XML con Namespaces
NodeList listaNodosNS = doc.getElementsByTagNameNS("http://www.latascadexela.es","etiquetaConNamespace");
for(int i=0;i<listaNodosNS.getLength();i++){
   Node nodo = listaNodosNS.item(i);
   if (nodo instanceof Element){
      System.out.println(nodo.getAttributes().getNamedItem("descripcion").getTextContent());
   }
}

Este método puede parecer la solución a todos tus problemas, pero ¿qué ocurre si tenemos etiquetas con el mismo nombre en distintas partes del XML? Por ejemplo, supongamos el XML siguente:

<?xml version="1.0" encoding="UTF-8"?>
<libros>
   <prestamo>
      <libro isbn="978-84-8346-520-2">
         <titulo>El Ocho</titulo>
         <autor>Katherine Neville</autor>
         <anyo>1988</anyo>
         <editorial>Ballantine Books</editorial>
      </libro>
      <libro isbn="84-226-6765-7">
         <titulo>Qumrán</titulo>
         <autor>Eliette Abécasis</autor>
         <anyo>1997</anyo>
         <editorial>Ediciones B</editorial>
      </libro>
   </prestamo>
   <venta>
      <libro isbn="84-675-069-X">
         <titulo>Memorias de Idhún I - La Resistencia</titulo>
         <autor>Laura Gallego García</autor>
         <anyo>2005</anyo>
         <editorial>Ediciones SM</editorial>
      </libro>
      <libro isbn="978-84-8346-808-1">
         <titulo>Next</titulo>
         <autor>Michael Crichton</autor>
         <anyo>2008</anyo>
         <editorial>DeBolsillo</editorial>
      </libro>
   </venta>
</libros>

Si buscamos las etiquetas que se llamen libro obtendremos tanto los libros en préstamo como los libros en venta. ¿Cómo diferenciamos cuáles son unos y cuáles otros? Evidentemente, preguntando por el padre getParentNode(). Pero hay una forma más sencilla, y es aquí cuando entra en escena nuestro amigo XPath.

Buscando con XPath

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 pero aplicado a XML.

Básicamente se trata de describir una ruta a través del árbol de nodos como si cada etiqueta fuera un directorio. Por ejemplo, /etiquetaPrincipal/etiquetaHija. Esta ruta XPath seleccionaría la etiquetaHija de nuestro XML. Por supuesto, es mucho más complejo y pontente: hay funciones, caracteres comodín, condicionales,... Podría escribir mucho acerca de XPath pero creo lo mejor es que echéis un vistazo a la página de donde aprendí yo y de paso la guardéis en vuestros favoritos ya que es una buena web de referencia: Tutorial de XPath.

¿Cómo aplicamos XPath con DOM? Los siguientes ejemplos lo muestran:

Con JAXP

// Buscamos una etiqueta mediante XPath.
// Implementación de XPath por defecto en Java
Node etiquetaHija = (Node)(XPathFactory.newInstance().newXPath().evaluate("/etiquetaPrincipal/etiquetaHija", doc, XPathConstants.NODE));
if (etiquetaHija!=null){
   System.out.println(etiquetaHija.getTextContent());
}

Mediante el tercer parámetro del método evaluate, indicamos lo que esperamos recibir. Puede ser: booleano, lista de nodos, un nodo, un número o una cadena.

Si vamos a evaluar una misma expresión XPath sobre distintos Document es más eficiente compilar la expresión XPath y a partir del objeto XPathExpresion evaluar sobre los distintos Document:

XPathExpression xpathCompilada = XPathFactory.newInstance().newXPath().compile("/etiquetaPrincipal/etiquetaHija");
Node etiqueta1 = (Node)xpathCompilada.evaluate(doc1, XPathConstants.NODE);
Node etiqueta2 = (Node)xpathCompilada.evaluate(doc2, XPathConstants.NODE);
[..]
Node etiquetaN = (Node)xpathCompilada.evaluate(docN, XPathConstants.NODE);

Con Xerces

// Buscamos una etiqueta mediante XPath.
// Implementación de XPath de Xerces
Node etiquetaHija = XPathAPI.selectSingleNode(doc, "/etiquetaPrincipal/etiquetaHija");
if (etiquetaHija!=null){
   System.out.println(etiquetaHija.getTextContent());
}

Existen métodos distintos para obtener una lista de nodos o un único nodo. En este caso hemos usado el de seleccionar un único nodo.

Como podemos observar, el problema del XML de los libros se resuelve fácilmente con XPath. Podemos sacar la lista de libros en préstamo /libros/prestamo/libro y la lista de libros en venta /libros/venta/libro.

Modificando el XML

Como decía, con DOM también se puede modificar el XML. Esto se hace modificando el árbol de nodos. Por ejemplo, veamos cómo añadir una etiqueta nueva. Lo primero que hay que hacer es crearla usando el Document donde la vamos a insertar y después la añadimos como hija a otra etiqueta.

// Añadimos una nueva etiqueta al documento
// Primero creamos la etiqueta (element)
Element nuevaEtiqueta = doc.createElement("nuevaEtiqueta");
// Añadimos atributos
nuevaEtiqueta.setAttribute("atributoNuevo", "Es un nuevo atributo");
// Añadimos contenido
nuevaEtiqueta.setTextContent("Contenido dentro de la nueva etiqueta");
// después se la añadimos como hija a una etiqueta ya existente
etiquetaHija.appendChild(nuevaEtiqueta);

Creando XML desde cero

Con DOM también podemos crear un documento XML nuevo. Este sería el paso inverso al parseo, es decir, mapeo de objetos a XML. Para ello usamos el DocumentBuilder de JAXP.

// Vamos a crear un XML desde cero
// En este caso usamos DocumentBuilder
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
// Creamos el documento XML
Document docNuevo = docBuilder.newDocument();

Copiando nodos de un Document a otro

También podemos copiar o mover elementos de un Document a otro. Como decía en el post anterior, para que un nodo pueda ser añadido al árbol de nodos de un Document concreto ha de pertenecer al ámbito de dicho Document. Por ello lo primero es obtener una instancia del element que queremos copiar bajo el ámbito del Document sobre el que lo queremos pegar. Esto se hace mediante el método import, si queremos copiar, o mediante el método adopt, si lo que queremos es mover. Es decir, adopt elimina el elemento del Document anterior. Una vez hecho esto ya lo situamos donde deseemos.

// Incluso podemos copiar parte de otro documeto XML en este nuevo
// Vamos a copiar todo el XML parseado en este nuevo
// Primero obtenemos la etiqueta Raiz del XML parseado al principio
Element etiquetaRaizACopiar = doc.getDocumentElement();
// Luego la copiamos bajo nuestra etiqueta hijaRaiz, por ejemplo
Node etiquetaRaizCopiada = docNuevo.importNode(etiquetaRaizACopiar, true); // El segundo atributo indica si queremos copiar los hijos
// Ya tenemos una copia de la etiqueta en nuestro document. Ahora la situamos bajo etiquetaHija
hijaRaiz.appendChild(etiquetaRaizCopiada);

// ImportNode hace una copia, dejando el xml original intacto
// AdoptNode mueve, es decir, elimina los elementos del arbol original y los pega en el nuevo-
// Este método siempre es recursivo.
// Por ejemplo, adoptemos la etiquetaHija.
Node etiquetaHijaAdoptada = docNuevo.adoptNode(etiquetaHija);
// Ya tenemos la etiquetaHija en nuestro document y se ha eliminado del anterior.
// La situamos bajo la etiqueta raiz
raiz.appendChild(etiquetaHijaAdoptada);

Una vez ejecutado el ejemplo y si obtenemos la representación en formato de String de los dos Document veríamos lo siguiente:

XML parseado:
<?xml version="1.0" encoding="UTF-8"?>
<etiquetaPrincipal xmlns:xela="http://www.latascadexela.es">
   <!-- Comentario -->
   <xela:etiquetaConNamespace descripcion="etiqueta con un namespace"/>
</etiquetaPrincipal>

XML nuevo:
<?xml version="1.0" encoding="UTF-8"?>
<etiquetaRaiz>
   <etiquetaHijaRaiz>
      <etiquetaPrincipal xmlns:xela="http://www.latascadexela.es">
         <etiquetaHija atributo1="valorAtributo1" atributo2="valorAtributo2" id="1">
Texto dentro de la etiqueta hija
<nuevaEtiqueta atributoNuevo="Es un nuevo atributo">Contenido dentro de la nueva etiqueta</nuevaEtiqueta>
         </etiquetaHija>
         <!-- Comentario -->
         <xela:etiquetaConNamespace descripcion="etiqueta con un namespace"/>
      </etiquetaPrincipal>
   </etiquetaHijaRaiz>
   <etiquetaHija atributo1="valorAtributo1" atributo2="valorAtributo2" id="1">
Texto dentro de la etiqueta hija
<nuevaEtiqueta atributoNuevo="Es un nuevo atributo">Contenido dentro de la nueva etiqueta</nuevaEtiqueta>
   </etiquetaHija>
</etiquetaRaiz>

Como podemos observar, la etiquetaHija fue adoptada por el nuevo Documento y por tanto eliminada del original. Esto no ocurre con el resto de etiquetas ya que tan sólo fueron importadas.

Serializando el XML

Lo primero que hicimos fue pasar un fichero XML a un árbol DOM. A partir de ahí hemos aprendido a modificar el árbol, incluso a crear nuevos documentos XML. Ahora vamos a ver como volver a convertir dicho árbol en un fichero XML. Digo fichero XML pero podría ser un String o un array de bytes. En general, cualquier cosa que pueda ser serializable mediante un Writer o un OutputStream.

// Vamos a convertir el arbol DOM en un String
// Definimos el formato de salida: encoding, identación, separador de línea,...
// Pasamos doc como argumento para tener un formato de partida
OutputFormat format = new OutputFormat(doc);
format.setLineSeparator(LineSeparator.Unix);
format.setIndenting(true);
format.setLineWidth(0);
format.setPreserveSpace(false);
// Definimos donde vamos a escribir. Puede ser cualquier OutputStream o un Writer
CharArrayWriter salidaXML = new CharArrayWriter();
// Serializamos el arbol DOM
XMLSerializer serializer = new XMLSerializer((Writer)salidaXML,format);
serializer.asDOMSerializer();
serializer.serialize(doc);
// Ya tenemos el XML serializado en el objeto salidaXML
System.out.println(salidaXML.toString());

Vemos como hemos hecho uso del OutputFormat para controlar el formato de salida. Podemos controlar si queremos que salga la declaración de xml (<?xml version ....), el encoding, la identación, si queremos un salto de línea tras cada etiqueta,....

Con esto hemos visto lo más importante de DOM según mi punto de vista. A continuación dejo el código completo tanto de la clase de ejemplo de uso de Xerces como la de JAXP.

ProcesaXMLJAXP.java

package es.latascadexela.xml.dom;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
* Clase de ejemplo de procesado de XML mediante DOM.
* Usa la implementación por defecto de Java (JAXP)
*
* @author Xela
*
*/
public class ProcesaXMLJAXP {

   public static void main(String[] args) {
      
      try {
            
         // Implementación DOM por defecto de Java
         // Construimos nuestro DocumentBuilder
         DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
         // Procesamos el fichero XML y obtenemos nuestro objeto Document
         Document doc = documentBuilder.parse(new InputSource(new FileInputStream("/ruta_a_fichero/fichero.xml")));
         
         // Obtenemos la etiqueta raiz
         Element elementRaiz = doc.getDocumentElement();
         // Iteramos sobre sus hijos
         NodeList hijos = elementRaiz.getChildNodes();
         for(int i=0;i<hijos.getLength();i++){
            Node nodo = hijos.item(i);
            if (nodo instanceof Element){
               System.out.println(nodo.getNodeName());
            }
         }
         
         // Buscamos una etiqueta dentro del XML
         NodeList listaNodos = doc.getElementsByTagName("etiquetaHija");
         for(int i=0;i<listaNodos.getLength();i++){
            Node nodo = listaNodos.item(i);
            if (nodo instanceof Element){
               System.out.println(nodo.getTextContent());
            }
         }
         
         // Buscamos una etiqueta dentro del XML con Namespaces
         NodeList listaNodosNS = doc.getElementsByTagNameNS("http://www.latascadexela.es","etiquetaConNamespace");
         for(int i=0;i<listaNodosNS.getLength();i++){
            Node nodo = listaNodosNS.item(i);
            if (nodo instanceof Element){
               System.out.println(nodo.getAttributes().getNamedItem("descripcion").getTextContent());
            }
         }
         
         // Buscamos una etiqueta mediante XPath.
         // Implementación de XPath por defecto en Java
         Node etiquetaHija = (Node)(XPathFactory.newInstance().newXPath().evaluate("/etiquetaPrincipal/etiquetaHija", doc, XPathConstants.NODE));
         if (etiquetaHija!=null){
            System.out.println(etiquetaHija.getTextContent());
         }
         
         // Añadimos una nueva etiqueta al documento
         // Primero creamos la etiqueta (element)
         Element nuevaEtiqueta = doc.createElement("nuevaEtiqueta");
         // después se la añadimos como hija a una etiqueta ya existente
         etiquetaHija.appendChild(nuevaEtiqueta);
         
      } catch (FileNotFoundException e) {
         e.printStackTrace();
      } catch (SAXException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      } catch (XPathExpressionException e) {
         e.printStackTrace();
      } catch (ParserConfigurationException e) {
         e.printStackTrace();
      }
      
   }

}

ProcesaXMLXerces.java

package es.latascadexela.xml.dom;

import java.io.CharArrayWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Writer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.parsers.DOMParser;
import com.sun.org.apache.xml.internal.serialize.LineSeparator;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import com.sun.org.apache.xpath.internal.XPathAPI;

/**
* Clase de ejemplo de procesado de XML mediante DOM.
* Usa la implementación de Xerces
*
* @author Xela
*
*/
public class ProcesaXMLXerces {

   public static void main(String[] args) {
      
      try {
         
         // Implementación DOM de Xerces
         // Creamos el parseador
         DOMParser parser = new DOMParser();
         // Procesamos el fichero XML
         parser.parse(new InputSource(new FileInputStream("/ruta_a_fichero/fichero.xml")));
         // Obtenemos el objeto Document
         Document doc = parser.getDocument();

         // Obtenemos la etiqueta raiz
         Element elementRaiz = doc.getDocumentElement();
         // Iteramos sobre sus hijos
         NodeList hijos = elementRaiz.getChildNodes();
         for(int i=0;i<hijos.getLength();i++){
            Node nodo = hijos.item(i);
            if (nodo instanceof Element){
               System.out.println(nodo.getNodeName());
            }
         }
         
         // Buscamos una etiqueta dentro del XML
         NodeList listaNodos = doc.getElementsByTagName("etiquetaHija");
         for(int i=0;i<listaNodos.getLength();i++){
            Node nodo = listaNodos.item(i);
            if (nodo instanceof Element){
               System.out.println(nodo.getTextContent());
            }
         }
         
         // Buscamos una etiqueta dentro del XML con Namespaces
         NodeList listaNodosNS = doc.getElementsByTagNameNS("http://www.latascadexela.es","etiquetaConNamespace");
         for(int i=0;i<listaNodosNS.getLength();i++){
            Node nodo = listaNodosNS.item(i);
            if (nodo instanceof Element){
               System.out.println(nodo.getAttributes().getNamedItem("descripcion").getTextContent());
            }
         }
         
         // Buscamos una etiqueta mediante XPath.
         // Implementación de XPath de Xerces
         Node etiquetaHija = XPathAPI.selectSingleNode(doc, "/etiquetaPrincipal/etiquetaHija");
         if (etiquetaHija!=null){
            System.out.println(etiquetaHija.getTextContent());
         }
         
         // Añadimos una nueva etiqueta al documento
         // Primero creamos la etiqueta (element)
         Element nuevaEtiqueta = doc.createElement("nuevaEtiqueta");
         // Añadimos atributos
         nuevaEtiqueta.setAttribute("atributoNuevo", "Es un nuevo atributo");
         // Añadimos contenido
         nuevaEtiqueta.setTextContent("Contenido dentro de la nueva etiqueta");
         // después se la añadimos como hija a una etiqueta ya existente
         etiquetaHija.appendChild(nuevaEtiqueta);
         
         // Vamos a convertir el arbol DOM en un String
         // Definimos el formato de salida: encoding, identación, separador de línea,...
         // Pasamos doc como argumento para tener un formato de partida
         OutputFormat format = new OutputFormat(doc);
         format.setLineSeparator(LineSeparator.Unix);
         format.setIndenting(true);
         format.setLineWidth(0);
         format.setPreserveSpace(false);
         // Definimos donde vamos a escribir. Puede ser cualquier OutputStream o un Writer
         CharArrayWriter salidaXML = new CharArrayWriter();
         // Serializamos el arbol DOM
         XMLSerializer serializer = new XMLSerializer((Writer)salidaXML,format);
         serializer.asDOMSerializer();
         serializer.serialize(doc);
         // Ya tenemos el XML serializado en el objeto salidaXML
         System.out.println(salidaXML.toString());
         

         // Vamos a crear un XML desde cero
         // En este caso usamos DocumentBuilder
         DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
         // Creamos el documento XML
         Document docNuevo = docBuilder.newDocument();
         // Creamos la etiqueta raiz
         Element raiz = docNuevo.createElement("etiquetaRaiz");
         docNuevo.appendChild(raiz);
         // Creamos una etiqueta hija de la etiqueta raiz
         Element hijaRaiz = docNuevo.createElement("etiquetaHijaRaiz");
         raiz.appendChild(hijaRaiz);

         // Incluso podemos copiar parte de otro documeto XML en este nuevo
         // Vamos a copiar todo el XML parseado en este nuevo
         // Primero obtenemos la etiqueta Raiz del XML parseado al principio
         Element etiquetaRaizACopiar = doc.getDocumentElement();
         // Luego la copiamos bajo nuestra etiqueta hijaRaiz, por ejemplo
         Node etiquetaRaizCopiada = docNuevo.importNode(etiquetaRaizACopiar, true); // El segundo atributo indica si queremos copiar los hijos
         // Ya tenemos una copia de la etiqueta en nuestro document. Ahora la situamos bajo etiquetaHija
         hijaRaiz.appendChild(etiquetaRaizCopiada);
         
         // ImportNode hace una copia, dejando el xml original intacto
         // AdoptNode mueve, es decir, elimina los elementos del arbol original y los pega en el nuevo-
         // Este método siempre es recursivo.
         // Por ejemplo, adoptemos la etiquetaHija.
         Node etiquetaHijaAdoptada = docNuevo.adoptNode(etiquetaHija);
         // Ya tenemos la etiquetaHija en nuestro document y se ha eliminado del anterior.
         // La situamos bajo la etiqueta raiz
         raiz.appendChild(etiquetaHijaAdoptada);
         
         // Veamos los dos XML el nuevo y cómo ha quedado el parseado.
         // Definimos donde vamos a escribir. Puede ser cualquier OutputStream o un Writer
         CharArrayWriter salidaXMLParseado = new CharArrayWriter();
         // Serializamos el arbol DOM
         XMLSerializer serializerXMLParseado = new XMLSerializer((Writer)salidaXMLParseado,format);
         serializerXMLParseado.asDOMSerializer();
         serializerXMLParseado.serialize(doc);
         // Ya tenemos el XML serializado en el objeto salidaXMLParseado
         System.out.println("XML parseado: \n"+salidaXMLParseado.toString());
         
         // Definimos donde vamos a escribir. Puede ser cualquier OutputStream o un Writer
         CharArrayWriter salidaXMLNuevo = new CharArrayWriter();
         // Serializamos el arbol DOM
         XMLSerializer serializerXMLNuevo = new XMLSerializer((Writer)salidaXMLNuevo,format);
         serializerXMLNuevo.asDOMSerializer();
         serializerXMLNuevo.serialize(docNuevo);
         // Ya tenemos el XML serializado en el objeto salidaXMLNuevo
         System.out.println("XML nuevo: \n"+salidaXMLNuevo.toString());         
         
         
      } catch (FileNotFoundException e) {
         e.printStackTrace();
      } catch (SAXException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      } catch (TransformerException e) {
         e.printStackTrace();
      } catch (ParserConfigurationException e) {
         e.printStackTrace();
      }
      
   }

}


Espero que este post tan largo sea de utilidad.

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 ;-)

sábado, 12 de julio de 2008

Java y XML: SAX (II)

Puesto que parece que eso de controlar el estado del procesado del xml que se comentaba en el post anterior no ha quedado muy claro, en este post pongo un ejemplo más real de procesado de XML.

En este ejemplo se trata de procesar el XML que define un libro:

<?xml version="1.0" encoding="UTF-8"?>
<libro isbn="978-84-8346-520-2">
   <titulo>El Ocho</titulo>
   <autor>Katherine Neville</autor>
   <anyo>1988</anyo>
   <editorial>Ballantine Books</editorial>
</libro>

Usaremos un manejador de eventos específico para este XML. Esta clase recibirá en el constructor un objeto de la clase Libro, en el cual se almacenará la información leida del XML.

Guardaremos en una variable lo último que se haya leído en el método characters y en el evento de fin de etiqueta, endElement, guardaremos este valor en la propiedad correspondiente del objeto libro.

Puesto que el ISBN nos viene en un atributo tendremos que procesarlo en el evento startElement de la etiqueta libro.


ManejadorLibro.java


package es.latascadexela.xml.sax;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
* Clase que procesa los eventos SAX al leer el xml de un libro.
* Guardará todos los datos obtenidos del xml en el objeto libro,
* el cual se pasa en el constructor.
*
* @author Xela
*
*/
public class ManejadorLibro extends DefaultHandler{

   private String valor = null;
   
   private Libro libro;
   
   public ManejadorLibro(Libro libro){
      this.libro=libro;
   }
   
   @Override
   public void startElement(String uri, String localName, String name,
         Attributes attributes) throws SAXException {
      
      // Limpiamos la variable temporal.
      valor=null;
      
      // Si la etiqueta es libro leemos el atributo isbn
      if(localName.equals("libro")){
         String isbn = attributes.getValue("isbn");
         // Lo guardamos en el objeto libro
         libro.setIsbn(isbn);
      }
   }
   
   @Override
   public void characters(char[] ch, int start, int length)
         throws SAXException {
      // Guardamos el texto en la variable temporal
      valor = new String(ch,start,length);
   }

   @Override
   public void endElement(String uri, String localName, String name)
         throws SAXException {
      // Según la etiqueta guardamos el valor leido
      // en una propiedad del objeto libro
      if (localName.equals("titulo")){
         libro.setTitulo(valor);
      }else if (localName.equals("autor")){
         libro.setAutor(valor);
      }else if (localName.equals("anyo")){
         libro.setAnyo(valor);
      }else if (localName.equals("editorial")){
         libro.setEditorial(valor);
      }
      
   }

}

Una vez tenemos construido el manejador, tendremos que crear la clase que procesa el XML utilizando nuestro manejador.


ProcesaLibro.java


package es.latascadexela.xml.sax;

import java.io.FileInputStream;
import java.io.IOException;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
* Clase que procesa un XML de un libro mediante el handler SAX ManejadorLibro
*
* @author Xela
*
*/
public class ProcesaLibro {

   public static void main(String[] args) {
      
      try {
         // Creamos nuestro objeto libro vacío
         Libro libro = new Libro();
         // Creamos la factoria de parseadores por defecto
         XMLReader reader = XMLReaderFactory.createXMLReader();
         // Añadimos nuestro manejador al reader pasandole el objeto libro
         reader.setContentHandler(new ManejadorLibro(libro));         
         // Procesamos el xml de ejemplo
         reader.parse(new InputSource(new FileInputStream("/ruta_hasta_fichero/libro.xml")));
         // Ya tenemos nuestro objeto libro con los valores obtenidos del xml
         // Lo imprimimos
         System.out.println(libro.toString());         
      } catch (SAXException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }

   }

}

Por último, la clase entidad que almacena los datos de un libro.

Libro.java


package es.latascadexela.xml.sax;

/**
* Clase que almacena los datos de un libro
*
* @author Xela
*
*/
public class Libro {
   
   private String isbn = null;
   private String titulo = null;
   private String autor = null;
   private String anyo = null;
   private String editorial = null;
   
   // Constructor por defecto
   public Libro() {
      
   }
   
   public String getIsbn() {
      return isbn;
   }
   
   public void setIsbn(String isbn) {
      this.isbn = isbn;
   }
   
   public String getTitulo() {
      return titulo;
   }
   
   public void setTitulo(String titulo) {
      this.titulo = titulo;
   }
   
   public String getAutor() {
      return autor;
   }
   
   public void setAutor(String autor) {
      this.autor = autor;
   }
   
   public String getAnyo() {
      return anyo;
   }
   
   public void setAnyo(String anyo) {
      this.anyo = anyo;
   }
   
   public String getEditorial() {
      return editorial;
   }
   
   public void setEditorial(String editorial) {
      this.editorial = editorial;
   }

   @Override
   public String toString() {
      StringBuilder sb = new StringBuilder();
      sb.append("\nISBN: "+isbn);
      sb.append("\nTítulo: "+titulo);
      sb.append("\nAutor: "+autor);
      sb.append("\nAño: "+anyo);
      sb.append("\nEditorial: "+editorial);
      
      return sb.toString();
   }
   
   

}

Al ejecutar el main tenemos que el objeto libro tiene toda la información obtenida del XML. Puesto que lo hemos imprimido por la salida estándar obtendríamos lo siguiente:

ISBN: 978-84-8346-520-2
Título: El Ocho
Autor: Katherine Neville
Año: 1988
Editorial: Ballantine Books

Espero que os haya aclarado algo.


martes, 8 de julio de 2008

Java y XML: SAX (I)

Tras lo comentado en el post Java y XML, me gustaría centrar la atención sobre el parseador SAX (Simple API for XML). Lo comento porque SAX fue el primer parseador de XML adoptado como API para XML en Java. Como se podrá ver a lo largo del post, SAX deja mucho que desear con respecto a otras APIs para XML como las basadas en DOM.

Como dije en el post anterior, SAX es un parseador de acceso serie y basado en eventos. Es decir, va recorriendo el xml poco a poco (en serie) y va generando eventos conforme va encontrando determinadas partes del xml, como el principio de etiquieta, el fin de etiqueta, texto, ....

A continuación expongo un ejemplo de uso de SAX. Se trata de dos clases: ManejadorEjemplo y ProcesaXML.

ManejadorEjemplo

Se trata de la clase que va a procesar cada evento que lance el procesador SAX. Basta con heredar del manejador por defecto de SAX DefaultHandler y sobreescribir los métodos correspondiente a los eventos deseados. En este caso he sobreescrito los más comunes:

  • startDocument: se produce al comenzar el procesado del documento xml.
  • endDocument: se produce al finalizar el procesado del documento xml.
  • startElement: se produce al comenzar el procesado de una etiqueta xml. Es aquí donde se leen los atributos de las etiquetas.
  • endElement: se produce al finalizar el procesado de una etiqueta xml.
  • characters: se produce al encontrar una cadena de texto.

Cada vez que el parseador encuentre una etiqueta se va a llamar a startElement. Sea la etiqueta que sea. Recibiremos como parámetro el nombre de la etiqueta pero no la etiqueta padre. Por tanto si tenemos etiquetas anidadas con el mismo nombre, debemos almacenar en un objeto persistente (al menos durante el procesado del documento xml) la ruta de etiquetas por la que vamos procesando. Es decir, un objeto que almacene el de estado en que se encuentra el procesado del documento.

Este inconveniente se acentúa al leer los valores de las etiquetas ya que al método characters se llama sin indicar el element al que se está accediendo. Para saber a qué etiqueta pertenece el texto debíamos haberlo marcado previamente al pasar por el startElement correspondiente. En este caso es imprescindible el objeto que nos marca el estado.

Por este tipo de cosas digo que este API es arcano y anticuado. Cuando veamos DOM, veremos que se puede tener acceso aleatorio a cualquier parte del documento XML, lo cuál nos evita toda esta parafernalia.

ManejadorEjemplo.java

package es.latascadexela.xml.sax;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
* Manejador de eventos SAX de ejemplo.
*
* @author Xela
*
*/
public class ManejadorEjemplo extends DefaultHandler{

   @Override
   public void startDocument() throws SAXException {
      System.out.println("\nPrincipio del documento...");
   }

   @Override
   public void endDocument() throws SAXException {
      System.out.println("\nFin del documento...");
   }

   @Override
   public void startElement(String uri, String localName, String name,
         Attributes attributes) throws SAXException {
      System.out.println("\nProcesando etiqueta...");
      System.out.println("\tNamespace uri: "+uri);
      System.out.println("\tNombre: "+localName);
      System.out.println("\tNombre con prefijo: "+name);
      
      //Recorremos los atributos
      System.out.println("\tProcesando "+attributes.getLength()+" atributos...");
      for(int i=0;i<attributes.getLength();i++){
         System.out.println("\t\tNombre: "+attributes.getQName(i));
         System.out.println("\t\tValor: "+attributes.getValue(i));
      }
      
      // También podemos obtener los atributos por nombre
      String valorId = attributes.getValue("id");
      if(valorId!=null){
         System.out.println("\tId: "+valorId);
      }

   }
   
   @Override
   public void characters(char[] ch, int start, int length)
         throws SAXException {
      System.out.println("\nProcesando texto dentro de una etiqueta... ");
      System.out.println("\tTexto: "+String.valueOf(ch, start, length));
   }

   @Override
   public void endElement(String uri, String localName, String name)
         throws SAXException {
      System.out.println("\nFin de etiqueta...");
      System.out.println("\tNamespace uri: "+uri);
      System.out.println("\tNombre: "+localName);
      System.out.println("\tNombre con prefijo: "+name);
   }

}


Como se puede observar la clase este ejemplo es muy tonto. Simplemente voy poniendo mensajes por la salida estándar conforme van saltando eventos.

ProcesaXML

Se trata del método main en el que se procesa el xml mediante nuestro handler ManejadorEjemplo.

ProcesaXML.java

package es.latascadexela.xml.sax;

import java.io.FileInputStream;
import java.io.IOException;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
* Clase que procesa un XML de ejemplo mediante el handler SAX ManejadorEjemplo
*
* @author Xela
*
*/
public class ProcesaXML {

   public static void main(String[] args) {
      
      try {
         // Creamos la factoria de parseadores por defecto
         XMLReader reader = XMLReaderFactory.createXMLReader();
         // Añadimos nuestro manejador al reader
         reader.setContentHandler(new ManejadorEjemplo());         
         // Procesamos el xml de ejemplo
         reader.parse(new InputSource(new FileInputStream("/ruta_hasta_el_fichero/fichero.xml")));
      } catch (SAXException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }

   }

}

Ejecutando nuestro parseador

Si realizamos el procesado de un xml de ejemplo como el siguiente:


<?xml version="1.0" encoding="UTF-8"?>
<etiquetaPrincipal xmlns:xela="http://www.latascadexela.es" >
   <etiquetaHija id="1" atributo1="valorAtributo1" atributo2="valorAtributo2">
      Texto dentro de la etiqueta hija
   </etiquetaHija>
   <!-- Comentario -->
   <xela:etiquetaConNamespace descripcion="etiqueta con un namespace"/>   
</etiquetaPrincipal>

podemos observar que obtenemos el siguiente resultado:


Principio del documento...

Procesando etiqueta...
   Namespace uri:
   Nombre: etiquetaPrincipal
   Nombre con prefijo: etiquetaPrincipal
   Procesando 0 atributos...

Procesando texto dentro de una etiqueta...
   Texto:
   

Procesando etiqueta...
   Namespace uri:
   Nombre: etiquetaHija
   Nombre con prefijo: etiquetaHija
   Procesando 3 atributos...
      Nombre: id
      Valor: 1
      Nombre: atributo1
      Valor: valorAtributo1
      Nombre: atributo2
      Valor: valorAtributo2
   Id: 1

Procesando texto dentro de una etiqueta...
   Texto:
      Texto dentro de la etiqueta hija

Procesando texto dentro de una etiqueta...
   Texto:
   

Fin de etiqueta...
   Namespace uri:
   Nombre: etiquetaHija
   Nombre con prefijo: etiquetaHija

Procesando texto dentro de una etiqueta...
   Texto:
   

Procesando texto dentro de una etiqueta...
   Texto:
   

Procesando etiqueta...
   Namespace uri: http://www.latascadexela.es
   Nombre: etiquetaConNamespace
   Nombre con prefijo: xela:etiquetaConNamespace
   Procesando 1 atributos...
      Nombre: descripcion
      Valor: etiqueta con un namespace

Fin de etiqueta...
   Namespace uri: http://www.latascadexela.es
   Nombre: etiquetaConNamespace
   Nombre con prefijo: xela:etiquetaConNamespace

Procesando texto dentro de una etiqueta...
   Texto:    

Procesando texto dentro de una etiqueta...
   Texto:


Fin de etiqueta...
   Namespace uri:
   Nombre: etiquetaPrincipal
   Nombre con prefijo: etiquetaPrincipal

Fin del documento...


Como se puede observar aparece mucho la llamada al método characters. No sólo justo dentro de las etiquetas sino también como separación entre una etiqueta y otra. Se puede deducir que el control del elemento que se está procesando es imprescindible para casi cualquier XML.

Por otro lado, el comentario xml ni lo ha detectado. SAX2 ya tiene un juego de eventos más completo y se pueden detectar comentarios, CDATA, etc.

Una vez visto esto, como resumen podemos enumerar las ventajas e inconvenientes de usar SAX:

Ventajas
  • No consume mucha memoria ya que va procesando el documento en serie
Inconvenientes
  • Es poco intuitivo para el desarrollador.
  • Requiere una serie de controles de estado adicionales que complican el procesado.
  • No permite construir XML. Sólo permite parsearlos.
  • No permite el acceso aleatorio al documento xml.

Como conclusión final, decir que sólo se puede justificar su uso desde el punto de vista del rendimiento al tratar XMLs grandes, ya que al acceder en serie al xml no necesita mantener en memoria todo el XML o representación equivalente, como hace DOM. En el resto de los casos, es mejor usar alguna API basada en DOM.

Supongo que la gente de saxproject.org tendrá una opinión muy diferente. Simplemente remarcar que lo aquí escrito es tan sólo mi opinión, la cuál está fundamentada en mi experiencia personal con Java y el mundo XML. Espero que te haya sido útil.

Más en la segunda parte.

viernes, 4 de julio de 2008

Equivalencia entre artefactos maven de spring 2.0 a 2.5

Este post es más bien un recordatorio para mí, pues siempre me cuesta encontrar el post donde viene explicado. De todas formas, si a alguien le ayuda, pues mejor.

El caso es que en el paso de spring 2.0 a 2.5 han reorganizado los artefactos y hay algunos nuevos y otros que han desaparecido. A continuación os pongo las equivalencias entre los artefactos de spring 2.0 y los de spring 2.5:

  • spring-jmx y spring-remoting han sido movidos a spring-context(jmx y non-HTTP remoting support) y a spring-web (HTTP remoting support).
  • spring-support ha sido renombrado a spring-context-support
  • spring-dao y spring-jca se han fusionado en spring-tx
  • Los artefactos relacionados con ORM, como spring-jdo, spring-jpa, spring-hibernate3, spring-toplink y spring-ibatis, han sido movidos a spring-orm
  • spring-mock ha sido renombrado a spring-test
  • El resto se siguen llamando igual.
Por último, comentar que al parecer la versión 2.5 es compatible hacia atrás con la versión 2.0. Para pasar varios proyectos de spring 2.0 a 2.5 me ha bastado con cambiar la versión de los artefactos que conservan el nombre y los que no lo hacen, simplemente meter su equivalente. Aún así, no está de más echar un vistazo al changelog para ver si nos puede afectar algún cambio.

Más información en la documentación oficial de Spring.

jueves, 3 de julio de 2008

Java y XML

El lenguaje XML se ha consolidado como uno estándares por excelencia en el intercambio de datos. Es ampliamente usado en el mundo de Internet, pero también lo es en otros tipos de aplicaciones como hojas de cálculo, bases de datos, etc. El objetivo de este post no es comentar qué es XML y las ventajas que su uso conlleva. Para eso hay otras páginas que lo explican muy bien. El objetivo de esta serie posts (serán más de uno) es, una vez te hayas familiarizado con XML, explicar cómo hacer uso de este metalenguaje desde aplicaciones Java.


Las APIs que yo he usado para el tratamiento de XML son las siguientes: SAX, DOM, JDOM y XOM. Tanto SAX(Simple API for XML Parsing) como el estándar DOM(Document Object Model) fueron implementadas por Java mediante la API JAXP (Java Api for XML Processing). Son los más antiguos y desde mi punto de vista arcaicos.

Por un lado SAX está hecho sólo para "parsear" (procesar) documentos XML y no permite la creación de los mismos. Por otro DOM, aunque ya permite creación de documentos XML, es un estándar independiente del lenguaje (JAXP implementa este estándar en Java), con las limitaciones que esto conlleva. JDOM y XOM son APIs basadas en DOM pero diseñadas específicamente para Java y por tanto son más eficientes y más fáciles de usar desde el punto de vista del programador Java. En ciertas ocasiones, operaciones de 10 líneas en DOM se implementan en 2 líneas de código en JDOM o XOM.

Podemos dividir estas 4 APIs en dos grupos: SAX en un lado y DOM, JDOM y XOM en otro. La diferencia estriba en que mientras SAX implementa un procesado de documentos de acceso en serie y basado en eventos, el resto mapea el documento XML en un arbol de objetos.

Dicho de otro modo, SAX procesa el documento xml secuencialmente y conforme se va encontrado partes del xml (comienzo de una etiqueta, fin de una etiqueta) van saltado eventos que son recogidos por una clase (manejador de eventos) la cuál actua de una forma u otra en función del evento. Por ejemplo, veremos más adelante que en el evento comienzo de una etiqueta se pueden leer los atributos de la misma o que en el evento fin de una etiqueta se puede leer el contenido de ella.

Por otro lado DOM, JDOM y XOM procesan el documento XML al completo y generan un arbol de nodos que representa el documento XML. Este árbol puede ser leído, modificado y de nuevo serializado a cadena o fichero XML. Incluso es posible crear un árbol desde cero, es decir, no hay que partir de un fichero o documento XML ya creado.

Como se puede deducir el uso de DOM, JDOM y XOM es más potente que el de SAX. Aunque como todo, nada es del todo malo o del todo bueno. El consumo de memoria de DOM,JDOM y XOM es mucho mayor que el de SAX, ya que mantienen todo el arbol de nodos en memoria. SAX puede venir bien para el procesado de documentos XML muy grandes ya que va procesando el XML evento por evento.

En próximos posts entraré en más profundidad en cada una de estas 4 APIs y expondré ejemplos de uso de las mismas. Espero que sean de utilidad para alguien: