XML para todos

Tras muchos meses de parón en el blog abordo un nuevo tema: escribir objetos en XML y recuperar dichos objetos.

Con las habituales herramientas: eclipse, maven, etc, etc.

La especificación JAXB con la definición de como se han de hacer los binding entre java y los objetos xml es parte de la especificación oficial desde java 1.6. Esto es importante si estás por ejemplo en el eclipse y en tu build path tienes una JVM 1.5 ya que alguna clase podría no estar disponible.

Esto me recuerda la cantidad de truños que se ven por ahí por no tener bien configurado maven, el plugin de maven en el eclipse, los proyectos maven... etc.

El proyecto base será el "QuickStart" de maven. Una simple aplicación de consola, que realice operaciones de marshalling , esto es, escribir un objeto como xml y un un-marshalling recuperar un objeto como xml para tener el objeto java. El XML lo vuelco sobre un archivo, pero es razonablemente trivial volcarlo sobre "otra cosa" y lo recupero desde el mismo archivo.

Le añado dos pequeñas "complicaciones", la primera es volcar un array, de un objeto que contiene otros objetos y que una de las propiedades deseamos que sea una fecha y que en el xml dicha fecha tenga un formato controlado:

ejemplo:

El archivo pom.xml sólo tiene una dependencia:

Resaltar que estoy asumiendo que la JVM es una 1.6 o superior. El Eclipse Mars que utilizo me "añade" por defecto un jre 1.5 al build path.

El wrapper para el array:


package es.sinjava.equisemele.domain;

import java.util.ArrayList;
import java.util.List;

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

@XmlRootElement
public class CollFilm extends ArrayList<Film> {
private static final long serialVersionUID = 1L;

@XmlElement(name = "film")
public List<Film> getMe() {
   return this;
   }
 
 
El elemento film:
 
package es.sinjava.equisemele.domain;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Film {
   private String title;
   private String director;
   @XmlJavaTypeAdapter(value = DateAdapter.class)
   private Date startDate;
   private int stars;
   @XmlElement
   private Medio medio;
......
}
 
Y el elemento medio:
package es.sinjava.equisemele.domain;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Medio {
   private boolean dvd;
   private boolean vhs;
   private boolean digital;

.....
}

Y el código del adapter que no da el formato deseado a la fecha en el XML:
package es.sinjava.equisemele.domain;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DateAdapter extends XmlAdapter<String, Date> {
   private SimpleDateFormat sdf = new SimpleDateFormat("dd/MMM/yyyy");
   @Override
   public Date unmarshal(String v) throws Exception {
      return sdf.parse(v);
   }

   @Override
   public String marshal(Date date) throws Exception {
      return sdf.format(date);
   }
}



Y por último la clase con el método "main"  que ejecuta el código que crea la colección, le inserta los dos elementos, lo vuelca en un archivo y un Writer para la consola.

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

      JAXBContext context = JAXBContext.newInstance(CollFilm.class);
      Marshaller marshaller = context.createMarshaller();
      marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
      CollFilm multiFilms = new CollFilm();

      Film film = new Film();
      film.setTitle("Blade Runner");
      film.setDirector("Ridley Scott");
      film.setStars(4999);
      film.setStartDate(new Date());
      Medio medio = new Medio();
      medio.setDigital(true);
      medio.setDvd(!false);
      medio.setVhs(true);
      film.setMedio(medio);
      multiFilms.add(film);

      File tempFile = File.createTempFile("xmlexample", ".xml");
      Writer writer = new StringWriter();
      marshaller.marshal(multiFilms, tempFile);
      marshaller.marshal(multiFilms, writer);

      // Unmarshalling
      Unmarshaller unMarshaller = context.createUnmarshaller();

      CollFilm respuesta = (CollFilm) unMarshaller.unmarshal(tempFile);
      System.out.println("Recuperado" + respuesta.get(0).getDirector());
      System.out.println(writer);
   }
}


Una vez ejecutado en el archivo de la carpeta temporal, que es donde creamos el archivo, deberíamos tener el contenido mostrado también en la consola:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<collFilm>
  <film>
    <title>Blade Runner</title>
    <director>Ridley Scott</director>
    <startDate>25/Oct/2015</startDate>
    <stars>4999</stars>
    <medio>
      <digital>true</digital>
      <dvd>true</dvd>
      <vhs>true</vhs>
    </medio>
  </film>
</collFilm>

Creo que el código es lo bastante corto para no necesitar mucha explicación.
Para convertir un objeto a XML necesitamos un marshaller. Dicho Marshaller lo creamos a través de un contexto de JAXB (Java Architecture for XML Binding ) y para el proceso inverso un unmarshaller. 

(Entrada pendiente de revisar)


Comentarios