Cómo funciona JAXB
Este capítulo describe brevemente como usar JAXB para:
- Unir el esquema a las clases Java.
- Construir representaciones de datos que siguen las reglas definidas en el esquema.
- Usar los Datos en una aplicación.
Introducción
Para comenzar a construir una aplicación JAXB todo los que necesitamos es un esquema XML.
La versión 1,0 de JAXB requiere que el esquema sea un DTD, según lo definido en la especificación XML 1,0, pero
versiones posteriores probablemente utilizarán otros lenguajes del esquema.
Después de obtener nuestro DTD, construimos y usamos una aplicación JAXB con estos pasos:
- Escribir el esquema de unión, un documento XML que contiene instrucciones de cómo unir un esquema a las clases.
Por ejemplo, el esquema de unión podría contener una instrucción sobre a qué tipo primitivo se debe unir un valor de atributo
en la clase generada.
- Generar los ficheros fuente de Java usando el compilador de esquema, que toma el DTD y el esquema de unión como
entrada de información. Después de compilar el código fuente, podemos escribir una aplicación basada en las clases que
resulten.
- Con nuestr aplicación, podemos construir un árbol de objetos Java que representen datos XML que son válidos con el DTD:
- ejemplarizando las clases generadas, o
- invocación método unmarshal de una clase generada y pasarlo en el documento.
El método unmarshal toma un documento válido XML y construye una representación de árbol de
objetos. El árbol de objetos se llama un árbol de contenido.
- Usar nuestra aplicación para acceder a los daros del árbol de contenidos para acceder a los datos del árbol de
contenido y modificar los datos del árbol.
- También podemos generar un documento XML desde el árbol de contenido llamando al método marshal
sobre el objeto raíz del árbol.
Las tres siguientes secciones entran en más detalle sobre la construcción y utilización de aplicacions JAXB.
La sección Unir un Esquema a las Clases describe los pasos 1 y 2. La sección
Construir Representaciones de Datos describe el paso 3. La sección
Trabajar con Datos describe los pasos 4 y 5.
Unir un Esquema a las Clases
Para unir un DTD a las clases, utilizamos el lenguaje de unión y el compilador de esquema, ambos incluidos con JAXB. El lenguaje de
unión es un lenguaje basado en XML que utilizamos para escribir al esquema de unión, el que contiene las instrucciones para unir el
DTD a las clases. El compilador del esquema genera un conjunto de ficheros fuente Java dedese el DTD basandose en las instrucciones
que proporciononamos en el esquema de unión. Después de que se generen los ficheros fuente Java, podemos compilarlos usando el
compilador de Java como lo haríamos con cualquier aplicación Java.
No necesitamos proporcionar una instrucción de unión para cada declaración del DTD para generar las clases Java. El compilador de
esquema hace ciertas asunciones basadas en el DTD si nuestro esquema de unión no especifica completamente cómo debería unirse cada
declaración del DTD al código. Por ejemplo, el compilador de esquema utiliza un algoritmo general de mapeo de nombres para unir nombres
XML a nombres que son aceptables en el lenguaje de programación Java. En este caso, podemos utilizar el esquema de unión para hacer que
el compilador de esquema genere nombres diferentes. Hay muchas otros personalizaciones que podemos hacer con el esquema de unión,
incluyendo:
- Nombrar el paquete, las clases derivadas y los métodos.
- Asignar tipos a los métodos dentro de las clases derivadas.
- Elegir los elementos a unir a las clases.
- Decidir cómo unir cada declaración de atributo y de elemento a una propieddad en la clase de contenido apropiada.
- Crear constructores personalizador, interfaces, y enumeraciones.
- Elegir el tipo de cada valor de atributo o especificación de contenido.
Puedes encontrar más informacion sobre como escribir un esquema de unión en el capítulo Unir un
Esquema a las Clases. La Difura 3-1 ilustra el proceso de generación de las clases:
A menos que lo especifiquemos de otra manera en nuestro esquema de unión, el compilador de esquema genera una clase para cada elemento
cuyo contenido contenga otros elementos. Dentro de una clase, el compilador de esquema genera las propiedades,
que son los métodos que utilizamos para tener acceso al contenido de los elementos hijos y a los valores de atributos. Estos métodos
devuelven y aceptan diversos tipos, dependiendo de tipo de declaración en el esquema. Por ejemplo, los elementos con contenido
#PCDATA están limitados a las propiedades que aceptan y devuelven valores String.
Como un ejemplo de unir un esquema a las clases, consideremos una versión simplificada del DTD del capítulo anterior:
<!ELEMENT book (title, author, chapter+) >
<!ELEMENT title (#PCDATA) >
<!ELEMENT author (#PCDATA)>
<!ELEMENT chapter (#PCDATA) >
En muchos casos, el compilador de esquema puede generar una unión apropiada incluso cuando el esquema de unión no incluye una
instrucción de unión para una declaración determinada del DTD. De hecho, para generar las clases del DTD book,
todo lo que necesitamos en nuestro esquema de unión es:
<xml-java-binding-schema>
<element name="book" type="class" root="true" />
</xml-java-binding-schema>
Para estos DTD y esquema de unión, el compilador de esquema genera una clase Book con este
contructor y estas propiedades:
public Book();
public String getTitle();
public void setTitle(String x);
public String getAuthor();
public void setAuthor(String x);
public List getChapter();
public void deleteChapter();
public void emptyChapter();
Recuerda que el elemento chapter en el modelo de contenido book
tenía un + como indicador de ocurrencia:
<!ELEMENT book (title, author, chapter+) >
Observa que este ejemplar de elemento chapter en el contenido del elemento
book está unido a una List de propiedades. Anteriormenteo dijimos
que todos los elementos simples están limitados a las propiedades de String. Esta declaración sigue
siendo válida para el elemento chapter. La lista que devuelve getChapter
es una lista de valores String, cada uno de los cuales representa un ejemplar distinto del elemento
chapter. Observa también que el esquema de unión no hizo referencia al indicador de ocurrencia después
del ejemplar del elemento chapter. Éste es un ejemplo de cómo el compilador de esquema considera las
especificaciones del DTD así como las instrucciones de unión del esquema de unión al generar las clases.
Las uniones por defecto que el compilador de esquema asume generalmente son adecuadas para DTDs simple como el ejemplo
book. Un DTDs más complicado muy probablemente requerirá instrucciones de unión más completas. Por ejemplo,
consideremo el DTD book con un modelo de elección adicional que contiene los elementos simples
prologue y preface:
<!ELEMENT book (title, author, (prologue | preface), chapter+) >
...
<!ELEMENT prologue (#PCDATA) >
<!ELEMENT preface (#PCDATA) >
Usando el mismo esquema de unión con este nuevo DTd, el compilador de esquema produciría este constructor y esta propiedad:
public void Book();
public List getContent();
public void deleteContent();
public void emptyContent();
En este ejemplo, la propiedad representa el modelo de contenido completo del elemento book. Esta clase
de propiedades no es muy útil si deseamos tener acceso a un trozo determinado del contenido. Esta es la razón por la cual escribiremos
un esquema de unión. Con el esquema de unión, podemos hacer muchas personalizaciones para requisitos particulares de nuestrasclases,
incluyendo la definición de la forma en que los grupos de modelo están unidos a las clases, creando interfaces, y convirtiendo tipos.
El capítulo Unir un Esquema a las Clases muestra cómo especificar estas personalizaciones
para requisitos particulares en el esquema de unión. Un esquema de unión que podríamos escribir para este DTD sería:
<xml-java-binding-schema>
<element name="book" type="class" root="true">
<content> <element-ref name="title" />
<element-ref name="author" />
<choice property="prologue-or-preface" />
</content>
</element>
</xml-java-binding-schema>
La declaracón choice une el modelo de grupo choice a un objeto
property dentro de la clase Book. Los elementos
prologue y preface son unidos a clases separadas. Las declaraciones
element-ref declarations unirán los elementos a propiedades dentro de la clase
Book.
Basándose tanto en el esquema como el DTD, el compilador de esquema asume que el nombre de la clase deseada es
Book y que los elemento simples se unen de acuerdo a estas declaracioens de unión:
<element name="title" type="value" />
<element name="author" type="value" />
<element name="prologue" type="class" />
<element name="preface" type="class" />
<element name="chapter" type="value" />
Desde el DTD y el esquema de unión, el compilador de esquema genera una clase Book con este constructor
y estas propiedades:
public void Book();
public String getTitle();
public void setTitle(String x);
public String getAuthor();
public void setAuthor(String x);
public List getChapter();
public void deleteChapter();
public void emptyChapter();
public MarshallableObject getPrologueOrPreface();
public void setPrologueOrPreface(MarshallableObject x);
La propiedad prologue-or-preface devuelve y acepta un MarshallableObject,
que representa objetos que pueden ser empaquetados y desempaquetados. La razón por la que este tipo de propiedad no es un
String es porque no podemos determinarse si el String es un prólogo o un
prefacio; con MarshallableObject, si podemos porque será un objeto prologue
o un objeto preface.
Los grupos modelo del elemento raíz en estos ejemplos de DTDs son más simples que el que se han mostrado en el ejemplo del capítulo
anterior. La especificación de modelo de grupo XML 1,0 es muy compleja, reflejando el número infinito de formas en que los datos pueden
ser ordenados y representados. El siguiente capítulo explica más detalladamente cómo unir modelos de grupo más complicados.
Construir Representaciones de Datos
Las clases Java que genera el compilador de esquema implementan y extienden las clases e interfaces del marco de trabajo de unión. Este
marco de trabajo es el API de tiempo de ejecución que usan las clases generadas para soportar tres operaciones primarias:
- Desempaquetar: el proceso de producir u árbol de contenidos desde un documento XML.
- Validacióm: el proceso de verificar que la representación de objetos Java está conforme a las reglas especificadas en el DTD.
- Empaquetar: el proceso de producir un documento XML desde objetos Java.
Para realizar estas operaciones, cada clase generada contiene métodos para empaquetar datos, validar contenidos, y extienden
métodos del marco de trabajo de unión que realizan el empaquetamiento.
Desempaquetar
Con los métodos unmarshal, podemos construir un árbol objetos Java desde documentos XML que son ejemplares
del esquema usado para generar las clases. El árbol de objetos construido con JAXB se llama un árbol de contenido.
Cada objeto del árbol corresponde a un elemento del documento XML. De forma semejantemente, cada objeto del árbol es un ejemplar de una
clase del conjunto de clases generadas. También podemos construir un árbol de contenido ejemplarizando objetos de las clases porque el
árbol de contenido une el documento y las clases. El capítulo Construir Representaciones de
Datos demuestra cómo utilizar el desmpaquetamiento y la ejemplarización para construir un árbol de contenido.
tree.
Validación
El proceso de desempaquetamietno realiza validación mientras está construyendo el árbol de contenido, por eso es imposible desempaquetar
un documento XML a un árbol de contenido que es inválido con respecto al DTD. Podemos realizar validación en caulquier momento después
de haber construido el árbol de contenido usando ls métodos validate o
validateThis en cada clase generada. El método validate valida completamente
el subárbol enraizado en el objeto raíz sobre el que le hemos llamado; el método validateThis sólo
valida un objeto del árbol.
Empaquetar
Tanto si construímos el árbol de contenido usando desempaquetamietno o ejemplarización , podemos empaquetar el árbol a un nuevo documento
XML usando lo método marshal. Esto significa que JAXB también permite que creemos
nuevos documentos XML que son válidos con respecto al DTD fuente. Las proceso de empaquetado comprueba si el árbol de contenido se ha
validado antes de empaquetarlo en caso de que hayamos realizado cambios a los objetos del árbol. Así pues, igual que es imposible
desempaquetar un documento inválido, es imposible empaquetar un árbol de contenido inválido.
La Figura 3-2 ilustra dos formas para construir representaciones de datos.
Continuando con el ejemplo book, podemos desempaquetar este documento usando la clase
Book que generamos:
<book>
<title>Duke: My Life and Times</title>
<author>Duke</author>
<chapter>The First Six Years ... </chapter>
</book>
El método unmarshal devolverá un objeto Book, digamos
dukeBook , que es la raíz del árbol de contenido. Una vez que tenemos el árbol, podemos empezar a trabajar con
los datos.
Trabajar con Datos
Podemos trabajar con los objetos del árbol de contenido igual que lo haríamos con cualquier objeto Java. De esta forma,
JAXB proporciona un interfaz de programación Java de datos XML, que podemos utilizar para integrar datos XML en
las aplicaciones Java.
para tener acceso al contenido del árbol, utilizamo las propieades de las clases generadas. Por ejemplo, para obtener el nombre del
autor del dukeBook: "My Life and Times", simplemente simplemente llamamos a getAuthor
en el objeto dukeBook. Digamos que el libro de Duke fue realmente escrio por un escritor fantasma.
Para alisar las cosas, podríamos llamar a setAuthor("Duke et. el al.") en el objeto
dukeBook. Para validar el árbol modificado, podemos llamar a validate
sobre dukeBook, y empaquetarlo en un nuevo documento XML, llamando a
dukeBook.marshal().
Para proporcionar funcionalidades especificaas de la aplicación, podemos extender las clases en vez de sólo utilizarlas directamente.
Por ejemplo, además de para acceder a un trozo de datos, también podríamos querer realizar algún cálculo con los datos o, en el caso de
dukeBook, agregar los datos a un catálogo. Podemos proporcionar estas funcionalidades en una subclase de
una clase derivada. El capítulo Construir Representaciones de Datos utiliza el ejemplo
libro de cheques descrito anteriormente en el Escenario 1: La sección "Balance del Libro de Cheques" utiliza las clases generadas
directamente para acceder directamente a los datos de la transacción y cómo ampliarlos para balancear nuestro libro de cheques.
Limitaciones
Como es versión de JAXB es una versión early-access, tiene algunas limtiaciones, que se
corregirán en versiones futuras. Algunas de estas limitaciones incluyen:
- Soporte para un sólo lenguaje de esquema:
Mientras se desarrollan más lenguajes de esquema y se definen las especificaciones existentes del lenguaje de esquema, los desarrolladores
de JAXB intentarán soportar más lenguajes de esquema en versiones posteriores. Por ahora, el sublenguaje del DTD
XML 1,0 es una opción natural para un lenguaje de esquema porque es el lenguaje de esquema más ampliamente utilizado y permite que
JAXB sirva a un mayor número de desarrolladores.
- No soporta espacios de nombres XML:
Las DTDs XML y los Espacios de Nombres XML no funcionan bien Juntos. Como esta versión de JAXB requiere un DTD,
los Espacios de nombres no están soportados.
- No hay soporte para lo tipos Internal subsets, NOTATIONs,
ENTITY, ENTITIES, y NOTATION enumerados del
sublenguaje XML DTD 1.0:
Estos constructores no aparecen muy frecuentemente en DTDs, por eso los desarrolladores eligieron no soportarlos para simplificar la
especificación del Lenguaje de Unión.