El API JAXB
JAXB proporciona un forma rápida y conveniente de crear un mapeo de
dos vías entre documentos XML y objetos Java. Dando un DTD, el compilador
JAXB genera un conjunto de clases Java, que contienen todo
el código para analizar documentos XML basados en ese esquema. Un desarrollador usando
las clases generadas puede construir un árbol de objetos Java representando un documento
XML, manipular el contenido del árbol, y regenerar el documento XML desde el árbol.
Para empezar a usar una aplicación JAXB todo lo que necesitamos es un
esquema, que en la versión de JAXB actual, debe ser un DTD. Podemos
escribir nuestro propio DTD, u obtenerlo desde cualquier otro lugar, como un repositorio de
DTDs estándars al que se accede a través de JAXR.
Una vez que tenemos nuestro DTD, lo unimos a un conjunto de clases realizando los
siguientes pasos:
- Escribir un esquema de unión, que contiene las instrucciones para unir el esquema a las
clases. El esquema de unión está escrito en un lenguaje de unión basado en XML, que está
incluido con el JAXB.
- Ejecutar el compilador de esquema, que toma un DTD y un esquema de unión y genera las
clases a partir de ellos. Cada clase que genere el compilador tiene un método
get y otro método set.
Cuando se crea e inicializa con datos un ejemplar de la clase, podemos usar estos métodos
accesores para acceder a los métodos. Un conjunto de métodos accesores se llama una
propiedad.
Generar Clases desde un DTD
Como ejemplo de generación de clases desde un DTD, consideremos el siguiente DTD, que
se llama "priceList.dtd".
<!ELEMENT priceList (coffee)+ >
<!ELEMENT coffee (name, price) >
<!ELEMENT name (#PCDATA) >
<!ELEMENT price (#PCDATA) >
El compilador de esquema JAXB es suficientemente poderoso como para hacer
asumciones razonables desde el DTD y el esquema de unión que especifica sólo el elemento raíz
del documento. Todo lo que necesitamos especificar en el esquema de unión es que el elemento
price es convertido a una propiedad que devuelve y acepta un
BigDecimal:
...
<element name="priceList" type="class" class="PriceList" root="true" />
<element name="price" type="value" convert="BigDecimal" />
<conversion name="BigDecimal" type="java.math.BigDecimal" />
...
Desde este DTD y este esquema de unión, el compilador de esquema genera una clase
PriceList y una clase Coffee.
La clase PriceList incluye un constructor y una lista de
propiedades, a las que está unida el elemento coffee. La
clase Coffee contiene un constructor y una propiedad para
representar el nombre del café y una propiedad para representar el precio. Los métodos
accesores para el precio son:
BigDecimal getPrice();
void setPrice(BigDecimal x);
Las dos clases también contienen métodos para desempaquetar, validar y empaquetar.
Desempaquetar es el proceso de construir una represención objeto del dato XML. La
validación es el proceso de chequear si el objeto está conforme a las especificaciones
del DTD. Empaquetar es el proceso de generar datos XML desde una representación
objeto.
Construir Representaciones Objeto de Datos XML
Después de generar nuestras clases, podemos escribir aplicaciones Java que las usen para
construir representaciones objeto del documento XML que sean válidos con respecto al DTD.
Cada objeto corresponde a un elemento del documento XML. De forma similar, cada objeto
es un ejemplar de una clase del conjunto de clases generadas. Como los objetos se mapean
tanto al documento como a las clases, tenemos dos formas diferentes de construir el árbol
de objetos Java: desempaquetando un documento XML válido, o ejemplarizando objetos desde
las clases. De esta forma, JAXB nos permite procesar documentos XML
existentes y crer nuevos datos XML ejemplarizando las clases generadas.
Supongamos que tenemos este documento XML:
<priceList>
<coffee>
<name>Arabica</name>
<price>13.50</price>
</coffee>
<coffee>
<name>Mocha Java</name>
<price>11.95</price>
</coffee>
<coffee>
<name>Sumatra</name>
<price>12.50</price>
</coffee>
</priceList>
Para desempaquetar este documento XML, creamos un stream de entrada desde él y llamamos
al método unmarshal de la clase
PriceList:
FileInputStream fis = new FileInputStream("priceList.xml");
PriceList myPrices = PriceList.unmarshal(fis);
Ahora tenemos un árbol de objetos Java con el objeto myPrices
como la raíz del árbol.
Supongamos que queremos crear nuestra propia lista de precios de cafés como un documento
XML. Primero cramos el árbol de objetos por ejemplarización y luego empaquetamos el árbol
en un documento XML. Para crear un árbol de objetos usando ejemplarización, creamos un
objeto PriceList, obtenemos la lista de objetos
Coffee desde él, creamos un nuevo objeto
Coffee, y lo añadimos a la lista:
PriceList myNewPrices = new PriceList();
List listOfCoffees = myNewPrices.getCoffees();
Coffee zCoffee = new Coffee();
zCoffee.setName("Zapoteca");
zCoffee.setPrice("15.00");
listOfCoffees.add(zCoffee);
Una vez que tenemos el dato XML en forma de un árbol de objetos, podemos trabajar con
los datos como con cualquier otro objeto Java. De esta forma, JAXB
proporciona un interface de programación Java para XML y nos permite la integración de
datos XML dentro de apliaciones Java.
Acceder a Datos desde el Árbol de Objetos
Supongamos que queremos cambiar el precio de "Mocha Java" en el primer árbol de objetos
que creamos. Todo lo que hacemos es encontrar "Mocha Java" en la lista de cafés y seleccionar
un nuevo precio llmando al método setPrice sobre el objeto
Coffee:
List coffees = myPrices.getCoffees();
for (ListIterator i = coffees.listIterator(); i.hasNext(); ) {
Coffee myCoffee = (Coffee) i.next();
if (myCoffee.getName().equals("Mocha Java") ) {
myCoffee.setPrice("12.50");
}
}
Escribir Documentos XML desde el Árbol de Objetos
Siempre que usemos desempaquetamiento o ejemplarización para construir nuestra
representación objeto, podemos empaquetar los objetos en un documento XML, lo que
significa que JAXB también permite crear nuevos documentos XML que
son válidos con respecto al esquema.
Para empaquetar nuestro árbol de objetos modificados en un nuevo documento XML, creamos
un fichero XML y un stream de salida para llamar al método
marshal sobre el objeto
myNewPrices:
File newPrices = new File("newPriceList.xml");
FileOutputStream fos = new FileOutputStream(newPrices);
myNewPrices.marshal(fos);
Sumario
JAXB esencialmente proporciona un puente entre XML y la tecnología Java.
Al igual que un documento XML es un ejemplar de un esquema, un objeto Java es un ejemplar de una
clase. Así, JAXB nos permite crear objetos Java al mismo nivel conceptual
que los datos XML. Representar nuestros datos de esta forma nos permite manipularlos de la
misma forma que manipularíamos objetos Java, haciendo fácil el crear aplicaciones para procesar
datos XML. Una vez que tenemos nuestros datos en forma de objetos Java, es fácil acceder a ellos.
Además, después de trabajar con los datos, podemos escribir los objetos Java en un nuevo, y
válido documento XML. Con el sencillo acceso a datos XML que proporciona
JAXB, somos libres para escribir aplicaciones que realmente usen los datos,
en lugar de gastar tiempo en escribir código para analizar y procesar los datos.
Diferencias entre JAXP y JAXB
JAXP y JAXB sirven a muy diferentes propósitos. La
arquitectura o el API que elijamos depende de los requerimientos de nuestra aplicación. Una
ventaja de JAXP es que nos permite analizar y procesar los datos desde
el mismo conjunto de APIs. Si sólo queremos grabar unos pocos datos de un gran documento,
deberíamos usar un analizador SAX porque analiza los datos como un stream, l
o que es muy rápido. Si tenemos un documento que no es muy largo, y queremos añadirle o eliminarle
datos, deberíamos usar DOM. Aunque un árbol DOM puede
ocupar una gran cantidad de memoria, el API DOM incluye funciones comunes
para manipulación de árboles. Si queremos transformar los datos a otro formato, deberíamos usar
JAXP, que incluye el API transformer y una
transformación XSLT en la implementación de referencia que nos permite transformar documentos XML,
eventos SAX o árboles DOM. JAXP
también nos pemite la flexibilidad de validar los datos o no.
Si queremos construir una representación objeto de datos XML, pero necesitamos evitar las
limitaciones de memoria de DOM, deberíamos usar JAXB.
Las clases creadas con JAXB no incluyen capacidades de manipulación de árboles,
que es un factor que contribuye al pequeño uso de memoria de un árbol de objetos
JAXB. Otra ventaja de este tipo de árboles es que podemos añadir
árboles además de que los objetos hijos pueden tener más de un objeto padre. Además, procesar los
datos con JAXB es casi tan rápido como procesarlos con un analizador
SAX porque las clases generadas contienen toda la lógica del DTD, por lo tanto
evitan la interpretación dinámica que el analizador SAX debe realizar. El hecho
de que JAXB requiera un DTD no lo hace menos flexible que
JAXP, sino que además este requerimiento garantiza que sólo se procesarán
los datos que sean válidos. Esta garantía es muy importante, especialmente si una aplicación
recibe datos desde otra fuente. Si una aplicación no tiene un DTD, no puede determinar el
significado de los datos y cómo deberian ser procesados. Otra ventaja que
JAXB tiene sobre JAXP es que nos permite especificar
cómo se genera neustro código desde nuestro DTD, incluyendo los tipos de datos que un elemento
de unión acepta y devuelve.
Claramenete, JAXP y JAXB tienen sus propias ventajas y
desventajas, que nosotros necesitaremos considerar cuando elijamos el API para procesar datos
XML. Estas dos listas sumarizan las ventajas de JAXB y
JAXP para que podamos decidir cuál es el correcto para nuestra aplicacion:
Usaremos JAXB cuando queramos:
- Acceder a datos en memoria, pero no necesitemos capacidades de manipulación.
- Procesar sólo datos que son válidos.
- Convertir datos a diferentes tipos.
- Generar clases basadas en un DTD.
- Construir representaciones Objetos de datos XML.
Usaremos JAXP cuando queramos:
- Tener flexibilidad en la forma de acceder a nuestros datos: serialmente con SAX o
aleatoriamente en memoria con DOM.
- Usar el mismo código de proceso con documentos basados en diferentes DTDs.
- Analizar documentos que no sean necesariamente válidos.
- Aplicar transformaciones XSLT.
- Insertar o eliminar objetos desde un árbol de objetos que representa datos XML.