Java y XML con JAXB2

02:41AM dic 06, 2007 en categoria Java por Enrique Rodriguez Lasterra

Etiquetas:


Traducir, mapear, serializar, transformar todos estos verbos y algún otro se han usado para explicar la tantas veces repetida operación de convertir nuestros objectos Java a ficheros XML.

Las formas de realizar esta operación han sido múltiples, desde SAX y DOM, pasando por Jdom, hasta llegar a soluciones más automáticas como Castor-XML, XMLBeans o JAXB y mientras estas herramientas se modernizaban el propio XML también lo hacía evolucionando desde la meta-información basada en DTDs hacía el XMLSchema, que a día de hoy es el estándar. 

Centrándonos en JAXB2, este estándar Java (JSR-222) fue incluido dentro de JavaEE 5 y JavaSE 6.  Java Arquitecture for XML Binding 2 trae varias mejoras con respecto a su primera versión.

  • Soporte completo de XMLSchema, por lo que podremos importar a objetos Java cualquier fichero XML que este validado contra un XMLSchema.
  • En JAXB2 también es posible realizar la operación inversa, convertir en XML objects de clases Java previamente definidas. Gracias a las anotaciones esta tarea es muy sencilla.
  • Las clases Java generadas desde los XMLSchema son mucho (muchísismo) más sencillas en esta versión.
  • Mejor capacidad de validación mediante JAXP1.3.

En esta primer capítulo de Java y XML (espero que haya varios más) vamos a ver como desde un documento de definición de un fichero XML, XSD (Xml Schema Definition), podemos fácilmente leer y escribir ficheros XML.

Nuestro primer paso será descargar las librerías de JAXB2. JAXB2 fue incluido en Java 6, luego si usamos esta versión podremos ejecutar el código JAXB2, pero para poder usar la tarea de ANT, es necesario descargarse las librerías completas. Existen 2 versiones de JAXB2

  • JAXB2 2.0.X, es la versión incluida en JavaSE 6 y JavaSE 6 Update1
  • JAXB2 2.1.X, esta incluida en JavaSE 6u2 y posterior
Más adelante veremos los problemas que puede generar esta amalgama de versiones, para los objetivos de este post nos vale cualquiera de las dos, pero por aquello de estar a la última, usaremos la última versión disponible: 2.1.5.

Tras descargar el archivo de instalación en el lugar donde deseamos instalarlo, seguimos los pasos que nos indica el manual y ejecutamos

java -jar JAXB2_**(version)**.jar

con lo que nuestra versión de JAXB queda instalada en la carpeta jaxb-ri-**(version)**. Dentro de esta carpeta destacamos las carpetas bin y lib.

  • La carpeta bin cuenta con dos programas
    • XJC, compilador de XMLSchema que genera clases Java
    • Schemagen, TODO realizar la operación inversa a XJC, genera XMLSchema desde clases Java.
  • La carpeta lib cuenta con varios ficheros JAR
    • jaxb-api.jar, cuenta con los interfaces del API JAXB definidos en el paquete javax.xml.bind. Este fichero JAR no es necesario incluirlo si nuestra JDK es la versión 6 o superior.
    • jaxb-impl.jar, implementación de los interfaces
    • jaxb1-impl.jar, para cumpli dependencia hacía atrás con la anterior versión del estándard
    • jaxb-xjc.jar, clases de los programas XJC y Schemagen
    • activation.jar y jsr_173_1.0_api.jar son dependencias

 Una vez instalado JAXB2 podemos empezar a trabajar en el ejemplo. La base del ejemplo es un fichero XSD que define como son los XML que vamos a mapear con clases Java. Vamos a utilizar uno muy sencillo, pero si alguien tiene dificultades con XMLSchema le recomiendo (me encantan estos cursillos) seguir el curso de w3schools.com pero espero que le ejemplo sea suficientemente sencillo como para no desviarnos de la temática de JAXB. Veamoslo

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://weblogs.javahispano.org/lasterra/jaxb2"
targetNamespace="http://weblogs.javahispano.org/lasterra/jaxb2"
elementFormDefault="qualified">

<xs:element name="elemento-principal">
<xs:complexType>
<xs:choice>
<xs:element name="campo-uno" type="campo-uno"/>
<xs:element name="campo-dos" type="xs:string"/>
<xs:element name="campo-tres" type="xs:int"
minOccurs="1" maxOccurs="unbounded"/>
</xs:choice>
</xs:complexType>
</xs:element>

<xs:complexType name="campo-uno">
<xs:sequence>
<xs:element name="campo-uno-uno" type="xs:int"/>
<xs:element name="campo-uno-dos" type="xs:double"
minOccurs="1"/>
</xs:sequence>
<xs:attribute name="attr" type="xs:string"/>
</xs:complexType>
</xs:schema>

Una vez tenemos el XSD debemos compilarlo con la herramienta XJC. Podemos ejecutar esta herramienta desde la línea de comandos, pero encontrareis mucho más útil usar su tarea ANT.

<target name="compile-schema">
<property name="jaxb2.path" value="D:\\jaxb-ri-20070917"/>
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask">
<classpath>
<fileset dir="${jaxb2.path}/lib" includes="*.jar"/>
</classpath>
</taskdef>
<xjc schema="schema.xsd"
package="org.javahispano.weblogs.lasterra.jaxb2.ejemplo"
destdir="${basedir}/src"/>
</target>

Como resultado de la ejecución de esta tarea de ANT se generan tres clases Java

  • La primera clase es ObjectFactory, que estará presente en todos nuestros proyectos donde JAXB2 entra en juego. Como su nombre indica su función es crear los objetos de nuestro esquema.
  • Las otras dos clases son ElementoPrincipal y CampoUno, que son los dos elementos complejos de nuestro esquema.

Si nos fijamos en el código de estas dos clases vemos que esta adornado de anotaciones java, pero a la vez es un código muy sencillo y fácil de leer, prácticamente estamos ante un JavaBean/POJO normal y corriente. Esto se hace patente a la hora de trabajar en la lectura y escritura de los archivos XML. Veamos antes las anotaciones de la clase ElementoPrincipal

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"campoUno",
"campoDos",
"campoTres"
})
@XmlRootElement(name = "elemento-principal")
public class ElementoPrincipal {

@XmlElement(name = "campo-uno")
protected CampoUno campoUno;
@XmlElement(name = "campo-dos")
protected String campoDos;
@XmlElement(name = "campo-tres")
protected List<BigInteger> campoTres;

Solo nos queda ver como trabajan estas clases anotadas para escribir y leer ficheros XML. Para hacer posible la operación de Marshall y Unmarshal debemos hacer uso de JAXBContext. Esta clase es la encargada de procesar las clases Java y realizar la serialización de las objetos. Para obtener una instancia de este objeto debemos indicar el/los paquetes de las clases anotadas. Una vez tenemos nuestro contexto JAXB podemos obtener un objeto Marshaller encargado de escribir XML a través del método marshall. Veamoslo con un ejemplo:

public static final void main(String[] args){
ObjectFactory factoria = new ObjectFactory();
ElementoPrincipal elementoPrincipal = factoria.createElementoPrincipal();
CampoUno campoUno = factoria.createCampoUno();
campoUno.setAttr("Esto es un attributo string");
campoUno.setCampoUnoDos(new Double(9.5));
elementoPrincipal.setCampoUno(campoUno);
elementoPrincipal.setCampoDos("Con esto acaba el ejemplo");
//Es curioso como se tratan las listas
//seguro que esperabas un metodo addCapoTres
elementoPrincipal.getCampoTres().add(new Integer(1));

elementoPrincipal.getCampoTres().add(new Integer(2));
elementoPrincipal.getCampoTres().add(new Integer(3));
try {
JAXBContext jaxbContext = JAXBContext
.newInstance("org.javahispano.weblogs.lasterra.jaxb2.ejemplo");
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(elementoPrincipal, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}

Para la lectura de ficheros XML el proceso es similar pero a la inversa, realizando la operación unmarshall del interfaz Unmarshaller. Lo vemos con el ejemplo de lectura de un String y como obtener el valor del elemento campo-dos

String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
"<elemento-principal xmlns=\"http://weblogs.javahispano.org/lasterra/jaxb2\">" +
"<campo-uno attr=\"Atributo\">" +
"<campo-uno-dos>0</campo-uno-dos>" +
"</campo-uno>" +
"<campo-dos>EJEMPLO LECTURA</campo-dos>" +
"<campo-tres>9</campo-tres><campo-tres>8</campo-tres><campo-tres>7</campo-tres>" +
"</elemento-principal>";
try {
JAXBContext jaxbContext =
JAXBContext.newInstance("org.javahispano.weblogs.lasterra.jaxb2.ejemplo");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
ElementoPrincipal leido =
(ElementoPrincipal) unmarshaller.unmarshal(new StringReader(xml));
System.out.println("\n\n" + leido.getCampoDos());
} catch (JAXBException e) {
e.printStackTrace();
}

Más sencillo no puede ser, al menos no se me ocurre, aunque veremos en futuros capítulos algunos ejemplos con algo más de miga.

Comentarios:

Enviar un comentario:
Los comentarios han sido deshabilitados.