viernes, 24 de abril de 2009

Java: Castor y XML

Ya es tiempo de abrir un poco de espacio a lo que tanto me gusta y en lo que me "gano la vida", esencialmente es programación en Websphere Message Broker y Webshpere MQ, pero tambien un poco de java (que es lo que de verdad me gusta). Hace ya varios meses mientras hacia mis últimos desarrollos para el equipo al que pertenecía me tope con la misión de hacer un XML data binding (a lo que en un principio me dijeron "parser") para Java, dicha misión no fue tan sencilla pues ya habían desarrollos enteros en los cuales se utilizaba jdom, herramienta que es bastante buena pero requiere cierto esfuerzo y paciencia para leer xmls bastante complejos, además de ser ad-hoc.

Luego de realizar un esfuerzo con xtream, componente que parece muy útil e interesante pero muy poco flexible pues los nombres y el orden de los tags (que realmente puede llegar a importar) era aleatorio, realmente no se si utilice mal el componente, ya tendré tiempo para leer mas y lo comento. El punto es que se evaluaron a mi gusto muy pocos java xml binding, pero el que tomo la delantera por flexibilidad y por la capacidad de encapsulamiento que proporciona es castor-xml (aunque tambien existe el nada despreciable betwixt)

Castor-xml se basa en dos frameworks, Unmarshaller y Marshaller (xml-java y java-xml, respectivamente), en dichos frameworks lo que se encapsula es todo lo referente a la manipulación de xml.

Un ejemplo sencillo, supongamos que tenemos una clase (en java por supuesto), y que esta clase describe a una persona y contiene nombre, apellido, edad, dui, etc........ nos debemos limitar a usar atributos "simples" (String, enteros, ...)

package com.home;
import java.io.Serializable;
@SuppressWarnings(value={"serial"})
public class Persona implements Serializable {
private String nombre;
private String apellido;
private String genero;
private int edad;
public Persona(){
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellido() {
return apellido;
}
public void setApellido(String apellido) {
this.apellido = apellido;
}
public String getGenero() {
return genero;
}
public void setGenero(String genero) {
this.genero = genero;
}
public int getEdad() {
return edad;
}
public void setEdad(int edad) {
this.edad = edad;
}
}

La forma inmediata que castor proporciona para convertir el contenido del objeto se escribe de la siguiente forma, encapsulado en un método por su puesto:

public static String object2XML(Object object) throws MarshalException, ValidationException{
StringWriter writer = new StringWriter();
Marshaller.marshal(object, writer);
return writer.getBuffer().toString();
}

Y la forma de hacer Unmarshall es esta:

public static Object xml2Object(Reader xml, Class clazz) throws MarshalException, ValidationException{
return Unmarshaller.unmarshal(clazz, xml);
}

Utilizando el método object2XML

try{
Persona persona = new Persona();
persona.setApellido("Perez");
persona.setNombre("Juan");
persona.setEdad(33);
persona.setGenero("M");
System.out.println(XMLDataBinding.object2XML(persona));
}
catch (Exception e) {
log.error("Error!!!",e);
}
}

Nota: XMLDataBinding es la clase donde coloque los metodos que ya les mencione

Lo que tenemos como resultado es el siguiente xml:


Notar que los nombres de los campos corresponden a los nombres de los accesores de la clase persona y el tag root corresponde al nombre de la clase y ¿cuál es la razón por la que el campo edad resulta ser un atributo al convertirlo a xml?
Esta situación tiene su razón en la forma en que castor realiza el proceso de Introspección, cualquier variable miembro que sea de tipo primitivo por defecto será colocado como atributo, la forma de modificar esto es crear el archivo castor.properties en nuestro classpath y colocar la siguiente entrada:
org.exolab.castor.xml.introspector.primitive.nodetype=element
Con lo cual lograremos que todos los campos sean convertidos como elementos del xml, además por salud mental se debe de colocar la siguiente entrada a dicho archivo properties:
org.exolab.castor.xml.naming=mixed
El cual generará los tags del xml de forma más clara, de otra forma por defecto los nombres se separarán por un "-".

Convertir el xml que hemos generado a un objeto Persona sería solo cuestión de crear un StringReader con el xml y ejecutar el método xml2Object enviándole dicho reader y la clase sobre la que haremos el binding (Persona.class) y verán la magia.

En post posteriores intentaré explicar el proceso de Marshall y Unmarshall utilizando xml para mapear las clases y poder tomar completo control de la estructura de nuestros xmls ademas poder realizar la conversión de clases más complejas así como el uso de Handlers.

2 comentarios:

  1. Por ser novato no entendi muchas cosas de las que hablastes jajajajajajajajajajajaja

    ResponderEliminar
  2. vos dale, proba, en el post olvide mencionar que en el classpath necesitas los jars commons-loggin y opcionalmente xerces. Podes revisar tambien este link Releases que te da varias opciones de descarga de los jars

    ResponderEliminar