Tutorial Castor XML (II): Marshalling/Unmarshalling

Genéricamente se denomina “marshalling” al proceso de volcar la representación en memoria de un objeto a un formato que permita su almacenamiento o transmisión, siendo “unmarshalling” el proceso contrario. En Castor XML, se denomina marshalling a la creación de un fichero XML con los datos contenidos en objetos Java, y unmarshalling al proceso opuesto.

En este segundo capítulo del tutorial vamos a probar el marshalling/unmarshalling que Castor XML proporciona, usando el modelo de clases generado en el capítulo anterior lo que nos permitirá asegurar que siempre trabajaremos con XMLs que serán válidos según el XSD. No obstante, la potencia y flexibilidad de Castor XML radica en que se puede utilizar  cualquier modelo de clases para su marshalling/unmarshalling en XML, lo que implica que podemos usarlo en nuestros proyectos en cualquier momento con un impacto prácticamente nulo. Al final de este capítulo se comenta brevemente  un ejemplo de marshalling/unmarshalling de un modelo de clases cualquiera no vinculado a un XSD.

Para las pruebas, en primer lugar vamos a crear un proyecto Maven llamado pruebasCastor-21 y su pom sólo tendrá como dependencia nuestro jar del foro creado en el capítulo anterior. Puesto que las librerias  de Castor XML ya son dependencias de este jar no es necesario incluirlas en el pom, al igual que el plugin  castor-maven-plugin del que ya no vamos a hacer uso.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.danielme.blog.castorxml</groupId>
	<artifactId>pruebasCastor-21</artifactId>
	<version>1.0</version>
	<name>pruebasCastor-21</name>
	<description>Pruebas de marshalling/unmarshalling con Castor XML, modelo generado desde XSD.</description>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.build.mainClass>com.danielme.blog.castorxml.pruebas21.Main</project.build.mainClass>
	</properties>

	<licenses>
		<license>
			<name>El presente proyecto Maven es el código de ejemplo utilizado en el 
            tutorial "Introducción a Castor XML", publicado con licencia Creative Commons 	
            Reconocimiento-NoComercial-CompartirIgual 3.0  Unported en la web 	
            "http://danielme.com"</name>
		</license>
	</licenses>

	<build>
		<pluginManagement>
			<plugins>

				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-jar-plugin</artifactId>
					<version>2.3.2</version>
					<configuration>
						<archive>
							<manifest>
								<addClasspath>true</addClasspath>
								<mainClass>${project.build.mainClass}</mainClass>
							</manifest>
						</archive>
					</configuration>
				</plugin>

				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>2.3.2</version>
					<configuration>
						<source>1.5</source>
						<target>1.5</target>
					</configuration>
				</plugin>


				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>exec-maven-plugin</artifactId>
					<version>1.2.1</version>
					<executions>
						<execution>
							<goals>
								<goal>java</goal>
							</goals>
						</execution>
					</executions>
					<configuration>
						<mainClass>${project.build.mainClass}</mainClass>
					</configuration>
				</plugin>

			</plugins>

		</pluginManagement>

	</build>

	<dependencies>
		<dependency>
			<groupId>com.danielme.blog.castorxml</groupId>
			<artifactId>foro</artifactId>
			<version>1.0</version>
		</dependency>
	</dependencies>

</project>

Creamos el subdirectorio src/main/java, y ya estamos listos para generar e importar el proyecto en Eclipse.

$ mvn eclipse:eclipse

Por simplicidad, las pruebas se realizarán con la clase definida en el pom com.danielme.blog.castorxml.pruebas21.Main, así que creamos la ruta de paquetes y esa clase con un método main dentro de /src/main/java. El proyecto resultante tiene la siguiente estructura:

Marshalling

Para probar el volcado de los objetos con los datos correspondientes a un foro en un XML, en primer lugar vamos a crear dichos objetos. Para ello he creado el método foroDePrueba en la clase Main, que devuelve un objeto de la clase  Foro que se corresponde con el elemento raíz del XSD y  que, por lo tanto, es el objeto sobre el que se realizará el marshalling (y en cascada de todos sus campos).

private static Foro foroDePrueba() 
{
    Foro foro = new Foro();
    foro.setNombreForo("MI FORO");

    UsuarioRef usuarioRef1 = new UsuarioRef();
    usuarioRef1.setId(1L);

    UsuarioRef usuarioRef2 = new UsuarioRef();
    usuarioRef2.setId(2L);

    Usuario usuario1 = new Usuario();
    usuario1.setAlias("admin");
    usuario1.setEmail("admin@correo.es");
    usuario1.setFechaIngreso(new Date(new GregorianCalendar(2012, 0, 1).getTime()));
    usuario1.setId(1);
    usuario1.setPassword("QWERTY");
    usuario1.setRol(UsuarioTypeRolType.ADMIN);

    Usuario usuario2 = new Usuario();
    usuario2.setAlias("juan español");
    usuario2.setEmail("juanesp@correo.es");
    usuario2.setFechaIngreso(new Date(new GregorianCalendar(2011, 0, 2).getTime()));
    usuario2.setId(2);
    usuario2.setPassword("ASDFG");
    usuario2.setRol(UsuarioTypeRolType.USUARIO);

    Mensaje mensaje1 = new Mensaje();
    mensaje1.setId(1);
    mensaje1.setFecha(new Date(new GregorianCalendar(2011, 2, 1).getTime()));
    mensaje1.setContenido("mensaje 1");
    mensaje1.setUsuarioRef(usuarioRef1);

    Mensaje mensaje2 = new Mensaje();
    mensaje2.setId(2);
    mensaje2.setFecha(new Date(new GregorianCalendar(2011, 2, 2).getTime()));
    mensaje2.setContenido("mensaje 2");
    mensaje2.setUsuarioRef(usuarioRef2);

    Agradecimiento agradecimiento1 = new Agradecimiento();
    agradecimiento1.setId(1L);
    agradecimiento1.setUsuarioRef(usuarioRef1);

    Agradecimiento agradecimiento2 = new Agradecimiento();
    agradecimiento2.setId(2L);
    agradecimiento2.setUsuarioRef(usuarioRef2);

    Tema tema1 = new Tema();
    tema1.setId(1);
    tema1.setFecha(new Date(new GregorianCalendar(2011, 1, 1).getTime()));
    tema1.setTitulo("Título del tema 1");
    tema1.setContenido("contenido del tema 1");
    tema1.setUsuarioRef(usuarioRef1);

    Tema tema2 = new Tema();
    tema2.setId(2);
    tema2.setFecha(new Date(new GregorianCalendar(2011, 1, 2).getTime()));
    tema2.setTitulo("Título del tema 2");
    tema2.setContenido("contenido del tema 2");
    tema2.setUsuarioRef(usuarioRef1);

    Categoria categoria1 = new Categoria();
    categoria1.setId(1);
    categoria1.setNombre("JAVA");

    Categoria categoria2 = new Categoria(); 
    categoria2.setId(2);
    categoria2.setNombre("XML");

    foro.setCategorias(new Categorias());
    foro.getCategorias().addCategoria(categoria1);
    foro.getCategorias().addCategoria(categoria2);
    tema2.setMensajes(new Mensajes());
    tema2.getMensajes().addMensaje(mensaje1);
    tema2.getMensajes().addMensaje(mensaje2);
    mensaje1.setAgradecimientos(new Agradecimientos());
    mensaje1.getAgradecimientos().addAgradecimiento(agradecimiento1);
    tema1.setAgradecimientos(new Agradecimientos());
    tema1.getAgradecimientos().addAgradecimiento(agradecimiento2);
    tema1.setCategoriaId(1);

    foro.setTemas(new Temas());
    foro.getTemas().addTema(tema1);
    foro.getTemas().addTema(tema2);

    foro.setUsuarios(new Usuarios());
    foro.getUsuarios().addUsuario(usuario1);
    foro.getUsuarios().addUsuario(usuario2);

    return foro;
}

Una vez tengamos el objeto foro,  podemos realizar el marshalling fácilmente invocando un método estático de la clase org.exolab.castor.xml.Marshaller. Asimismo, conviene comprobar que la estructura de datos que vamos a volcar es válida según el XSD para asegurar que el XML generado también lo sea. Capturando las excepciones, el método main queda de la siguiente forma:

	public static void main(String[] args) throws Exception 
	{
		try
		{
			Foro foro = foroDePrueba();
			// asegura que el xml resultante es válido según el XSD
			if (!foro.isValid())
			{
				throw new Exception("los datos del foro no son válidos según el XSD");
			}
			FileWriter writer = new FileWriter("exportacionForo.xml");
			Marshaller.marshal(foro, writer);						
		}
		catch (IOException e1)
		{
			e1.printStackTrace();
		}
		catch (MarshalException e2)
		{
			e2.printStackTrace();
		}
		catch (ValidationException e3)
		{
			e3.printStackTrace();
		}
	}

Si se ejecuta esta aplicación de ejemplo desde Eclipse o la consola:

$ mvn clean package exec:java

como resultado se obtendrá en el directorio raiz del proyecto el fichero exportacionForo.xml que contendrá los datos del foro definidos en el método  foroDePrueba:

<?xml version="1.0" encoding="UTF-8"?>
<ExportacionForo xmlns="https://danielme.com/blog/castorxml"
	nombre="MI FORO">
	<temas>
		<tema id="1" fecha="2011-02-01" titulo="Título del tema 1"
			categoriaId="1">
			<contenido>contenido del tema 1</contenido>
			<agradecimientos>
				<agradecimiento>
					<id>2</id>
					<usuarioRef>
						<id>2</id>
					</usuarioRef>
				</agradecimiento>
			</agradecimientos>
			<usuarioRef>
				<id>1</id>
			</usuarioRef>
		</tema>
		<tema id="2" fecha="2011-02-02" titulo="Título del tema 2">
			<contenido>contenido del tema 2</contenido>
			<mensajes>
				<mensaje id="1" fecha="2011-03-01">
					<contenido>mensaje 1</contenido>
					<agradecimientos>
						<agradecimiento>
							<id>1</id>
							<usuarioRef>
								<id>1</id>
							</usuarioRef>
						</agradecimiento>
					</agradecimientos>
					<usuarioRef>
						<id>1</id>
					</usuarioRef>
				</mensaje>
				<mensaje id="2" fecha="2011-03-02">
					<contenido>mensaje 2</contenido>
					<usuarioRef>
						<id>2</id>
					</usuarioRef>
				</mensaje>
			</mensajes>
			<usuarioRef>
				<id>1</id>
			</usuarioRef>
		</tema>
	</temas>
	<usuarios>
		<usuario id="1" alias="admin" rol="ADMIN" fechaIngreso="2012-01-01"
			email="admin@correo.es" password="QWERTY" />
		<usuario id="2" alias="juan español" rol="USUARIO"
			fechaIngreso="2011-01-02" email="juanesp@correo.es" password="ASDFG" />
	</usuarios>
	<categorias>
		<categoria id="1" nombre="JAVA" />
		<categoria id="2" nombre="XML" />
	</categorias>
</ExportacionForo>

Unmarshalling

Realizar el proceso inverso, esto es, el unmarshalling, es también sumamente sencillo:

	//unmarshall
	FileReader fileReader = new FileReader("exportacionForo.xml");
	Foro unmarshal = (Foro) Unmarshaller.unmarshal(Foro.class, fileReader);
			
	//comprobamos algunos datos
	System.out.println("nombre : " + unmarshal.getNombreForo());
	System.out.println("temas  ");
	Iterator<? extends Tema> iterateTema = unmarshal.getTemas().iterateTema();
	while (iterateTema.hasNext())
	{
	    Tema next = iterateTema.next();
	    System.out.println("            + " + next.getTitulo() + " : " + next.getContenido());
	    if (next.getMensajes() != null)
	    {	
	        for (Mensaje mensaje : next.getMensajes().getMensaje())
		{
		    System.out.println("             + "  + mensaje.getContenido());
		}
	    }
	 }

En la salida estándar deberíamos ver lo siguiente:

Asimismo, al hacer unmarshalling Castor XML validará automáticamente que el XML sea correcto según el XSD, lanzando una excepción en caso contrario.

Ejemplo para cualquier modelo de clases

Vamos a suponer que, en lugar de partir de un esquema XSD, tenemos ya un modelo de clases en nuestro proyecto y queremos utilizar Castor XML para exportar/importar los datos contenidos en objetos  de esas clases. Pues bien, tal y como se comentó al comienzo de este capítulo, el código para realizar marshalling/unmarshalling es el mismo que el utilizado en el ejemplo anterior, y las clases del modelo pueden ser usadas directamente sin ninguna modificación.

Para probarlo, vamos a volver a crear un proyecto de ejemplo, llamado pruebasCastor-22, con Maven e importarlo en Eclipse. En este caso, en el pom se cambiará la dependencia de foro.jar por la de Castor XML.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0    	http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.danielme.blog.castorxml</groupId>
	<artifactId>pruebasCastor-22</artifactId>
	<version>1.0</version>
	<name>pruebasCastor-22</name>

	<description>Pruebas de marshalling/unmarshalling con Castor XML. Modelo de clases
  no basado en XSD.</description>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.build.mainClass>com.danielme.blog.castorxml.pruebas22.Main</project.build.mainClass>
	</properties>

	<licenses>
		<license>
			<name>El presente proyecto Maven es el código de ejemplo utilizado en el tutorial    "Introducción a Castor XML", publicado con licencia Creative Commons Reconocimiento-NoComercial-CompartirIgual 3.0  Unported en la web 	
      "http://danielme.com"</name>
		</license>
	</licenses>

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-jar-plugin</artifactId>
					<version>2.3.2</version>
					<configuration>
						<archive>
							<manifest>
								<addClasspath>true</addClasspath>
								<mainClass>${project.build.mainClass}</mainClass>
							</manifest>
						</archive>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>2.3.2</version>
					<configuration>
						<source>1.5</source>
						<target>1.5</target>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>exec-maven-plugin</artifactId>
					<version>1.2.1</version>
					<executions>
						<execution>
							<goals>
								<goal>java</goal>
							</goals>
						</execution>
					</executions>
					<configuration>
						<mainClass>${project.build.mainClass}</mainClass>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

	<dependencies>
		<dependency>
			<groupId>org.codehaus.castor</groupId>
			<artifactId>castor-xml</artifactId>
			<version>1.3.2</version>
		</dependency>
	</dependencies>

</project>


En el paquete com.danielme.blog.castorxml.modeloequipo se ubicarán las siguientes clases (descargar el código de ejemplo):

El proyecto importado en Eclipse queda tal que así:

Y la clase Main es similar a la del ejemplo anterior pero cambiando el modelo de clases para el foro por el de los equipos.

package com.danielme.blog.castorxml.pruebas22;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;

import com.danielme.blog.castorxml.modeloequipo.Entrenador;
import com.danielme.blog.castorxml.modeloequipo.Equipo;
import com.danielme.blog.castorxml.modeloequipo.Jugador;

/**
* Pruebas de marshalling/unmarshalling.
*
* @author danielme.com
*
*/
public class Main
{

  /**
  * @param args
  * @throws Exception
  */
  public static void main(String[] args)
  {
    String fichero = "exportacionEquipo.xml";
    Equipo equipo = equipoDePrueba();
    try
    {
      FileWriter writer = new FileWriter(fichero);
      Marshaller.marshal(equipo, writer);
      FileReader fileReader = new FileReader(fichero);
      Equipo unmarshal = (Equipo) Unmarshaller.unmarshal(Equipo.class, fileReader);
      System.out.println("EQUIPO : " + unmarshal.getNombre());
      System.out.println("entrenador: " + unmarshal.getEntrenador().getNombre() + " " + unmarshal.getEntrenador().getApellidos());
      System.out.println("jugadores  ");

      for (Jugador jugador: equipo.getJugadores())
       {
         System.out.println("            " + jugador.getDorsal() + ": " + jugador.getNombre() + " " + jugador.getApellidos());
       }
    }
   catch (IOException e1)
   {
     e1.printStackTrace();
   }
   catch (MarshalException e2)
   {
    e2.printStackTrace();
   }
   catch (ValidationException e3)
  {
   e3.printStackTrace();
  }

  }

private static Equipo equipoDePrueba()
{
 Equipo equipo = new Equipo();
 Jugador jugador1 = new Jugador();
 jugador1.setNombre("Juan");
 jugador1.setApellidos("Español");
 jugador1.setDorsal(1);
 jugador1.setEstatura(178);
 jugador1.setPeso(74.3F);
 jugador1.setId(1);
 Calendar fechaNac1 = new GregorianCalendar(1987, 2, 10);
 jugador1.setFechaNacimiento(fechaNac1);

 Jugador jugador2 = new Jugador();
 jugador2.setNombre("Miguel");
 jugador2.setApellidos("Pérez Pérez");
 jugador2.setDorsal(2);
 jugador2.setEstatura(174);
 jugador2.setPeso(71.1F);
 jugador2.setId(2);
 Calendar fechaNac2 = new GregorianCalendar(1985, 3, 7);
 jugador2.setFechaNacimiento(fechaNac2);

 Entrenador entrenador = new Entrenador();
 entrenador.setEquipo(equipo);
 entrenador.setId(14);
 entrenador.setNombre("Joaquín");
 entrenador.setApellidos("García");
 Calendar fechaNac3 = new GregorianCalendar(1964, 1, 2);
 entrenador.setFechaNacimiento(fechaNac3);

 List<Jugador> jugadores = new ArrayList<Jugador>();
 jugadores.add(jugador1);
 jugadores.add(jugador2);

 equipo.setId(1);
 equipo.setNombre("JAVA TEAM");
 equipo.setEntrenador(entrenador);
 equipo.setJugadores(jugadores);

 return equipo;
 }

}

Al ejecutarse esta clase, se creará el XML correspondiente (exportacionEquipo.xml) con los datos establecidos en el método Main.equipoDePrueba:

<?xml version="1.0" encoding="UTF-8"?>
<equipo>
	<jugadores xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:java="http://java.sun.com" xsi:type="java:com.danielme.blog.castorxml.modeloequipo.Jugador">
		<estatura>178</estatura>
		<peso>74.3</peso>
		<dorsal>1</dorsal>
		<apellidos>Español</apellidos>
		<nombre>Juan</nombre>
		<id>1</id>
	</jugadores>
	<jugadores xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:java="http://java.sun.com" xsi:type="java:com.danielme.blog.castorxml.modeloequipo.Jugador">
		<estatura>174</estatura>
		<peso>71.1</peso>
		<dorsal>2</dorsal>
		<apellidos>Pérez Pérez</apellidos>
		<nombre>Miguel</nombre>
		<id>2</id>
	</jugadores>
	<entrenador>
		<apellidos>García</apellidos>
		<nombre>Joaquín</nombre>
		<id>14</id>
	</entrenador>
	<nombre>JAVA TEAM</nombre>
	<id>1</id>
</equipo>

Y en la salida estándar, un resumen de los datos que han pasado primero por el marshalling y luego por el unmarshalling:

Nota: obsérvese que no se ha incluído en el XML el campo con la fecha de nacimiento  de tipo java.util.Calendar, en el siguiente capítulo veremos cómo solucionarlo.

Código de ejemplo

Descargar desde WordPress.comWordPress: debido a las limitaciones del servicio ofrecido por WordPress.com, el código de ejemplo está comprimido en formato zip pero con la extensión «.odt», por lo que hay que cambiar la extensión a «.zip» del fichero descargado o abrirlo directamente con el software adecuado.

  • ejemplo 1 (foro/XSD) aquí
  • ejemplo 2 (equipo) aquí

2 comentarios sobre “Tutorial Castor XML (II): Marshalling/Unmarshalling

  1. Hola que tal, disculpa, con el Marshal se puede agregar un nuevo elemento a un xml con datos? Gracias! Saludos!

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.