jueves, 24 de noviembre de 2011

JAXB: Leer y escribir ficheros XML

Muchas veces en nuestras aplicaciones debemos manejar documentos XML (Extensible Markup Language). Este lenguaje se ha convertido en un estándar para intercambio de datos entre programas y aplicaciones a través de Internet. En un esquema XML (o XSD) podemos definir los elementos que pueden aparecer en un documento XML así como las relaciones entre los mismos.

JAXB (Java Architecture for XML Binding) es un estándar Java para transformar un esquema XML (o XSD) en una representación a objetos java. Mediante la API de JAXB podemos mapear un objeto Java a un documento XML ("marshall") y el proceso contrario, es decir, a partir de un esquema XML crear su conjunto de objeto Java asociado ("unmarshall").


JAXB

Resumiendo lo que nos proporciona JAXB es:
  • Generación de objetos Java a partir de un XSD a través de un compilador
  • Proporciona capacidades de marshall/unmarshall (escribir fichero XML desde java y al contrario)
  • Integración con Maven a través de xjc (desde Java 1.6)
  • Validación de nuestro documento XML a partir de un XSD

Existen otras herramientas para manejar XML desde Java como por ejemplo  Xstream.  Mi amigo y compañero el señor Fuente lo explica perfectamente en un tutorial. Depende de los requisitos de nuestro proyecto deberíamos utilizar una u otra herramienta, puesto que cada una tiene sus ventajas e inconvenientes. Con Xstream no se pueden generar clases Java a partir de un esquema de datos dado y con JAXB sí. Sin embargo, Xstream es más sencillo de utilizar y las clases del modelo que se mapean a/desde XML no necesitan añadir etiquetas adicionales (pueden ser simples beans).


A continuación os voy a poner unos ejemplos con JAXB. Los ejemplos los he realizado con Eclipse 3.6 (Helios) y Java 1.6. En esta versión se incluyen las capacidades de JAXB (es una ventaja no añadir ninguna librería adicional).

El ejemplo consiste en crear dos entidades Java, Empresa y Empleado. Una Empresa está compuesta por un conjunto de Empleados. Las anotaciones que se utilizan en estas dos clases son las siguientes:


  • @XmlRootElement(name = "empleado").  Define que el elemento raíz del objeto Java se llamará empleado.
  • @XmlType(propOrder = { "dni", "nombre", "edad", "puesto" }). Define el orden de los elementos dentro de otro elemento. 
  • @XmlElement(name = "cargo"). Sirve para cambiar el nombre de un elemento en el documento XML. Por defecto el elemento aparece con el nombre del atributo de la clase Java pero podemos cambiarlo con esta etiqueta. En el ejemplo se cambia el nombre puesto por cargo. Se puede utilizar en los atributos tipo lista para indicar cómo se llamará cada elemento de la lista. 
  • @XmlElementWrapper(name = "empleados"). Para crear un wrapper llamado empleados que englobe la estructura XML de los objetos Empleado.

A continuación tenéis el código de la clase Empresa:



package es.jpascu.jaxb;

import java.util.ArrayList;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "empresa")
public class Empresa {
 private String cif;
 private String nombre;
 private ArrayList empleados;

 public String getCif() {
  return cif;
 }

 public void setCif(String cif) {
  this.cif = cif;
 }

 public String getNombre() {
  return nombre;
 }

 public void setNombre(String nombre) {
  this.nombre = nombre;
 }

 @XmlElementWrapper(name = "empleados")
 @XmlElement(name = "empleado")
 public ArrayList getEmpleados() {
  return empleados;
 }

 public void setEmpleados(ArrayList empleados) {
  this.empleados = empleados;
 }

}


Y el código de la clase Empleado:


package es.jpascu.jaxb;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "empleado")
@XmlType(propOrder = { "dni", "nombre", "edad", "puesto" })
public class Empleado {
 private String dni;
 private String nombre;
 private int edad;
 private String puesto;
 public String getDni() {
  return dni;
 }
 public void setDni(String dni) {
  this.dni = dni;
 }
 public String getNombre() {
  return nombre;
 }
 public void setNombre(String nombre) {
  this.nombre = nombre;
 }
 public int getEdad() {
  return edad;
 }
 public void setEdad(int edad) {
  this.edad = edad;
 }
 @XmlElement(name = "cargo")
 public String getPuesto() {
  return puesto;
 }
 public void setPuesto(String puesto) {
  this.puesto = puesto;
 }
 
 
}

Por último, ponemos el código de una clase de prueba que hace uso de la API de JAXB para rellenar el modelo de objetos Java  (empresa y empleados) y crear un XML a partir del mismo. Posteriormente, con el XML guardado en disco, se realiza el proceso contrario, cargar la información del XML en el objeto Empresa. El código lo tenéis  aquí:



package es.jpascu.jaxb;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class JaxbTest {
 private final static String XML_EMPLEADOS = "./info-empleados.xml";

 public static void main(String[] args) throws JAXBException, IOException {

  // Primero rellenamos los objetos Java y generamos un XML

  ArrayList empleados = new ArrayList();

  Empleado empleado1 = new Empleado();
  empleado1.setDni("12345678C");
  empleado1.setNombre("Carlos Pérez Ruíz");
  empleado1.setEdad(29);

  Empleado empleado2 = new Empleado();
  empleado2.setDni("87654321C");
  empleado2.setNombre("Claudia Ortiz Zaldo");
  empleado2.setEdad(31);

  Empresa empresa = new Empresa();
  empresa.setCif("A58818501");
  empresa.setNombre("TECNOMUR S.L.");
  empleados.add(empleado1);
  empleados.add(empleado2);
  empresa.setEmpleados(empleados);

  // Creamos el contexto e instanciamos el marshaller
  JAXBContext context = JAXBContext.newInstance(Empresa.class);
  Marshaller m = context.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
  m.marshal(empresa, System.out);

  Writer w = null;
  try {
   w = new FileWriter(XML_EMPLEADOS);
   m.marshal(empresa, w);
  } finally {
   try {
    w.close();
   } catch (Exception e) {
   }
  }

  // Ahora leemos el XML e instanciamos las clases Java
  System.out.println("Salida desde el fichero XML: ");
  Unmarshaller um = context.createUnmarshaller();
  Empresa empresa2 = (Empresa) um
    .unmarshal(new FileReader(XML_EMPLEADOS));

  for (int i = 0; i < empresa2.getEmpleados().toArray().length; i++) {
   System.out.println("Empleado " + (i + 1) + ": "
     + empresa2.getEmpleados().get(i).getNombre() + " con DNI "
     + empresa2.getEmpleados().get(i).getDni() + " y "
     + empresa2.getEmpleados().get(i).getEdad() + " años");
  }

 }
}



Referencias: Oracle | Vogella

7 comentarios:

  1. a mi me gustaria ver como esta escrito tu .xml

    ResponderEliminar
  2. a mi me gustaria ver como esta escrito tu .xml

    ResponderEliminar
  3. Muchas gracias! muy buen aporte y bien explicado

    ResponderEliminar
  4. Buen día, estoy tratando de usar su ejemplo aplicándolo a mi propio entorno y necesidad, usando una clase generada por el JAXB, pero la referencia que usted usa al final no la puedo usar ya que mi clase tiene otro nombre y no se que poner ahí, tengo un error de compilación, mucho agradeceré su ayuda

    ResponderEliminar