XMLBeans, una forma de mapear un XML en objetos Java

7
45710

Creación: 18-08-2007

Índice de contenidos

1. Introducción

Hay varias formas de trabajar con documentos XML. Una de ellas es mapear el XML en objetos Java, de forma que en vez navegar por el XML con un DOM o con XPath, directamente usamos objetos con sus getters y sus setters; lo cual puede resultar muy cómodo para trabajar con un XML dentro de nuestra aplicación.

Dentro de esta técnica podríamos decir que hay dos grandes posibilidades o tecnologías (hay más de dos, pero entre todas podríamos destacar estas por ser las más extendidas y estándar):

  • JAXB (Java Architecture for XML Binding): El es estándar de Java (lo podemos encontrar en el JEE 5). JSR 222 (http://jcp.org/en/jsr/detail?id=222).
  • XMLBeans: Podríamos decir que es el estándar de «facto» por estar altamente extendido. En un principio fue un proyecto de BEA, que posteriormente donaron a la comunidad Apache (http://xmlbeans.apache.org/).

Ambas tecnologías son muy parecidas y hacen prácticamente lo mismo. En este tutorial no voy a entrar en el debate de cual de las dos es mejor. Para eso no tenéis más que buscar en Google «xmlbeans vs jaxb» y encontraréis multitud de referencias.

En este tutorial simplemente vamos a ver una introducción a XMLBeans para ver como podemos obtener, a partir de un DTD, las clases Java que procesan los XML que cumplen ese DTD.

Vamos a hacer un ejemplo práctico donde generaremos las clases Java para procesar los XMLs que genera el Bugzilla como resultado de una búsqueda. Estas clases nos podrán servir para «alimentar» otras aplicaciones con los datos que genera el Bugzilla.

Dejaremos para otro tutorial hacer el mismo ejemplo con JAXB (a ver si alguien se anima y lo hace 😉

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Asus G1 (Core 2 Duo a 2.1 GHz, 2048 MB RAM, 120 GB HD).
  • Sistema Operativo: GNU / Linux, Debian (unstable), Kernel 2.6.22, KDE 3.5
  • Java 1.5
  • XMLBeans 2.3.0

3. Instalando XMLBeans

Nos descargamos la última release de
http://www.apache.org/dyn/closer.cgi/xmlbeans/binaries.

Y la descomprimimos, por ejemplo, en el directorio /opt:

$ cd /opt
$ tar -xzf /directorio_donde_hicimos_la_descarga/xmlbeans-2.3.0.tgz

Si queremos poder ejecutar desde línea de comandos las utilidades que vienen con XMLBeans, sería conveniente poner el directorio /opt/xmlbeans-2.3.0/bin en el PATH del sistema. Por ejemplo, en Debian podemos hacer:

$ export PATH=$PATH:/opt/xmlbeans-2.3.0/bin

A partir de este momento, en la consola donde hemos ejecutado este comando, podremos invocar las utilidades que vienen con XMLBeans sin necesidad de escribir su ruta completa.


4. Generando las clases Java a partir del esquema (XSD)

Sí, has leído bien, XMLBeans sólo es capaz de generar las clases Java a partir de un esquema (un fichero .xsd). Esto puede ser un problema si lo que tenemos es un DTD (como en el ejemplo que hemos planteado).

Aún así no hay que desesperar 😉 lo que vamos a hacer es pasar del DTD al XSD y del XSD a las clases Java.

4.1. Pasar del DTD al XSD

El DTD que utiliza Bugzilla lo podéis ver aquí.

En vez de hacer la conversión a mano, lo mejor es usar alguna herramienta que podamos encontrar por Internet. Yo he probado con estas y parece que funcionan correctamente:

  • http://www.hitsw.com/xml_utilites/ – Se trata de una página web donde, de forma gratuita, le indicamos el fichero y nos devuelve el XSD equivalente. Es la opción más cómoda, ya que no tenemos que instalar nada, y lo tendremos resuelto en un momento. Esta es la opción que he seguido yo.
  • http://www.syntext.com/products/index.htm#Dtd2Xs – Se trata de una herramienta tanto para Windows como para Linux. El XSD que genera esta aplicación es prácticamente idéntico a la primera opción que hemos comentado.

El XSD obtenido con la primera opción lo podéis encontrar aquí.

Si os fijáis en el XSD, se declaran todos los elementos como tipos complejos (como en un DTD no se puede especificar el tipo de los elementos, la herramienta a intentado escoger la solución más genérica). Esto no me convence porque es una complejidad innecesaria, así que he editado a mano el fichero generado para cambiar estos tipos complejos por tipos «xs:string» es decir, por cadenas de caracteres (para el ejemplo me va bien, pero podríais especificar el tipo que mejor se ajuste a vuestras necesidades).

El XSD simplificado lo podéis encontrar aquí.

4.2. Pasar del XSD a las clases Java

Para pasar del XSD a las clases Java vamos usar el comando scomp, que viene con XMLBeans. Haremos algo como:

scomp -src src -javasource 1.5 -out bugzilla-3.0-xmltypes.jar bugzilla-3.0-simple.xsd

  • -src src : con esto le estamos diciendo que queremos que nos generé el código fuente (los .java) en el directorio src. Esta opción no es obligatoria, es sólo por si luego tenéis curiosidad de ver el código que genera XMLBeans. Si no ponemos está opción se limitará a generar un .jar con las clases que necesitamos.
  • -javasource 1.5 : con esta opción le estamos indicando que tipo de código Java queremos que genere. Podemos elegir entre 1.4 o 1.5.
  • -out bugzilla-3.0-xmltypes.jar : estamos indicando el nombre del jar (por defecto xmltypes.jar) que se va a crear con las clases generadas y compiladas.
  • bugzilla-3.0-simple.xsd : el nombre del fichero XSD que queremos usar para generar las clases Java.

Si todo funciona correctamente deberíamos ver en la consola algo como:

Time to build schema type system:
0.782 seconds
Time to generate code: 0.811 seconds
Time to
compile code: 6.144 seconds
Compiled types to:
bugzilla-3.0-xmltypes.jar

Y deberíamos tener el jar bugzilla-3.0-xmltypes.jar con todas las clases necesarias para procesas los XMLs generados por el Bugzilla.


4.3. Definiendo el paquete para las clases generadas

Si nos fijamos en las clases generadas (no hay más que entrar en el directorio src), veremos que las ha puesto en el paquete noNamespace. Esto no es nada conveniente, y nunca debemos generar clases en el paquete noNamespace; ya que si en un mismo proyecto tenemos mas clases generadas con XMLBeans, pertenecientes a XMLs diferentes, todas estarían dentro del mismo paquete y podrían entrar en conflicto.

El hecho de que el paquete utilizado sea noNamespaces, se debe a que nuestro esquema no tenía definido el targetNamespace. Por ejemplo, si nuestro XSD hubiera tenido el siguiente tergetNamespace:

<xs:schema targetNamespace="http://autentia.com/xmlbeans/bugzilla"
        xmlns:cr="http://autentia.com/xmlbeans/bugzilla"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
    ...

las clases se hubieran generado en el paquete:

com.autentia.xmlbeans.bugzilla

Otra forma de especificar el paquete para las clases Java, bien si no tenemos definido el targetNamespace o bien porque queremos generarlas en otro paquete, sería usando un fichero para configurar como se debe hacer la generación de código. Este fichero debe tener la extensión .xsdconfig.

En nuestro caso, por ejemplo, si no quisiéramos añadir el targetNamespace a nuestro XSD, podríamos usar el siguiente fichero de configuración (bugzilla-3.0.xsdconfig):

<?xml version="1.0" encoding="UTF-8"?>
<xb:config xmlns:xb="http://xml.apache.org/xmlbeans/2004/02/xbean/config">
    <xb:namespace uri="##any">
        <xb:package>com.autentia.xmlbeans.generated.bugzilla30</xb:package>
    </xb:namespace>
</xb:config>

Nótese como en el atributo uri del elemento xb:namespace, se ha puesto el valor ##any. Este indica que, en ausencia de targetNamespace, se debe usar el paquete indicado en el elemento xb:package.

Si en vez de ##any hubiéramos especificado un namespace, lo que estaríamos haciendo es cambiar un nombre de paquete por otro.

Para más información sobre este fichero de configuración se puede consultar:
http://dev2dev.bea.com/lpt/a/8

Para lanzar la generación usando el fichero de configuración, lo añadiremos como último parámetro del comando scomp. Por ejemplo:

scomp -src src -javasource 1.5 -out
bugzilla-3.0-xmltypes.jar bugzilla-3.0-simple.xsd
bugzilla-3.0.xsdconfig

5. Ejemplo de uso de las clases generadas

Ahora que ya tenemos el jar con las clases generadas, ya podemos usarlo en nuestros proyectos.

Para leer uno de estos XMLs generados por el Bugzilla haríamos algo como:

...

// Leemos el XML y lo
convertimos en objetos
BugzillaDocument doc =
BugzillaDocument.Factory.parse(new File(file));

// Ahora
podemos trabajar con los objetos
final List<Bug> bugs =
doc.getBugzilla().getBugList();

...

Como se puede ver, es muy sencillo, ya que basta con ir llamando a los métodos de las clases generadas por XMLBeans.

6. Conclusiones

Gracias a XMLBeans hemos visto como en pocos minutos podemos generar un jar que nos permite operar con XMLs que sigan un determinado esquema, tanto para leerlos, como para escribirlos, …

El conocimiento de la existencia de este tipo de herramientas nos puede ahorrar mucho tiempo y quebraderos de cabeza.

7. Sobre el autor

Alejandro Pérez García, Ingeniero en Informática (especialidad de Ingeniería del Software)

Socio fundador de Autentia (Formación, Consultoría, Desarrollo de sistemas transaccionales)

mailto:alejandropg@autentia.com

Autentia Real Business Solutions S.L. – «Soporte a Desarrollo»

http://www.autentia.com

 

Alejandro es socio fundador de Autentia y nuestro experto en Java EE, Linux y optimización de aplicaciones empresariales. Ingeniero en Informática y Certified ScrumMaster. Seguir @alejandropgarci Si te gusta lo que ves, puedes contratarle para darte ayuda con soporte experto, impartir cursos presenciales en tu empresa o para que realicemos tus proyectos como factoría (Madrid). Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación.

7 COMENTARIOS

  1. Hola que pasaria si tengo una xml con la siguiente forma:

    LST-DSLPORTDETAILINFO::DEV=#HOSTNAME#,FN=#RACK#,SN=#SLOT#,PN=#PORT#:100::;

    como lo trabajaria un método para pasarle los parametros y q me devuelva el command completo.

  2. Hola, agraciada si me puedes ayudar, estoy trabajando en la desarrollo de una API en Java, para dar mi respuesta estoy utilizando Jersey para generar el xml, el problema que tengo es que mi en algunos casos usa servios de otra API la cual también da respuesta en XML, lo que quiero es incrustar esa repuesta como parte de mi respuesta xml, lo que no quiero es tener que hacer es un traductor de respuestas punto a punto ya que si lo hago así el funcionamiento de mi API se tambalearía ante cambios en la otra API y el mantenimiento seria de la API se convertiría en una esclavitud.

    «Aqui adentro introduzco todas mis respuestas»

    Ejemplo:

    llamada al api
    FIN
    todo salio bien

    Resultado 1
    Resultado 2
    Resultado 3
    Resultado 4

    Lo que yo quiero es que si la respuesta de la api externa es

    nombre 1
    apellido 1

    nombre 2
    apellido 2

    nombre 3
    apellido 3

    entonces poder darle al usuario de mi API la siguiente respuesta

    llamada al api
    FIN
    todo salio bien

    nombre 1
    apellido 1

    nombre 2
    apellido 2

    nombre 3
    apellido 3

    Gracias

    • Tu servicio debería llamar al otro servicio y luego hacer con esa respuesta lo que tu quieras, es decir, si quieres la puedes devolver directamente, no hace que la transformes, para que así un cambio en el otro API no afecte al tuyo.

  3. Hola

    Tengo un problema con un esquema XML el cual al intentar compilar me tira el siguiente error:

    warning: SchemaType Enumeration found with too many enumeration values to create a Java enumeration. The base SchemaType «T=c_CodigoPostal@http://www.sat.gob.mx/sitio_internet/cfd/catalogos» will be used instead

    En algun foro lei que XMLBeans tenia problemas con enumeraciones muy grandes el cual es mi caso. En otros foros he leido que con xbeans se puede solucionar el problema con esquemas muy grandes, no se que tan cierto sea ya que no he intentado descargarlo, por lo que agradeceria cualquier ayuda para poder compilar este esquema si es que hay alguna solucion.

      • Hola nuevamente

        Siguiendo con el problema mencionado anteriormente les comento que encontre un pequeño indicio de como resolver el problema en el siguiente link https://it.ojp.gov/NISS/kb/231 en donde explican que una posible solucion consiste en dividir en partes el esquema y despues aplicarles una union al momento de compilar lo cual no entiendo muy bien como hacerlo si alguien sabe como hacerlo le agradeceria cualquier informacion.

        De antemano gracias

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad