Construir Representaciones de Datos
Este capítulo explica cómo usar las clases que generamos en el capítulo anterior para:
- Despempaquetar un documento XML en un árbol de contenido.
- Ejemplarizar las clases para construir un árbol de contenido.
- Validar nuestro árbol de contenido contra el DTD.
- Empaquetar un árbol de contenido en un nuevo documento XML.
- Añadir un arbol de contenido a otro árbol de contenido.
El Ejemplar de Documento XML:march.xml
El documento march.xml es válido contra checkbook.dtd. Recordamos del
capítulo Antes de Empezar: Fundamentos de XML que un documento XML
debe tener un elemento raíz que incluya el resto de los elementos del documento. El elemento raíz del documento
march.xml es el elemento transactions porque
march.xml representa el conjunto de las transacciones para el mes de Marzo. Según lo mostrado
aquí en march.xml, sólo teníamos un depósito, un cheque, y una transacción de reintegro en el mes
de marzo:
<?xml version="1.0" encoding="US-ASCII"?>
<;transactions>
<;deposit category=salary >
<;date>03-14-2001<;/date>
<;name>Me<;/name>
<;amount>3000.00<;/amount>
<;/deposit>
<;check number="2" category="groceries">
<;date>03-15-2001<;/date>
<;name>Conglomerate Foods<;/name>
<;amount>34.95<;/amount>
<;pending/>
<;memo>food<;/memo>
<;/check>
<;withdrawal>
<;date>03-16-2001<;/date>
<;amount>20.00<;/amount>
<;/withdrawal>
<;/transactions>
Configurar Nuestra Aplicación
Antes de poder usar JAXB para construir representaciones de datos o trabajar con los
datos, primero necesitamos cear una aplicación Java que realice estas funciones. Para configurar nuestra
aplicación JAXB:
- Creamos un fichero llamado CheckbookApp.java
- Importamos estos paquetes:
import java.io.*;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.marshal.*;
Los dos últimos paquetes son parte del marco de trabajo de unión, que define los métodos
unmarshal, marshal, y
validate.
- Declaramos la clase CheckbookApp :
public class CheckbookApp {
}
- Inicializamos dos objetos Transactions:
public static Transactions marchTrans = new Transactions();
public static Transactions aprilTrans = new Transactions();
Necesitaremos reutilizar estos objetos en nuestra aplicación.
- Dentro de CheckbookApp, creamos nuestro método main:
public class CheckbookApp {
public static void main(String args[]) throws Exception {
}
}
El fichero CheckbookApp.java está localizado en el
directorio examples/checkbook de la descarga.
Construir un Árbol de Contenido
JAXB nos permite construir un árbol de contenido de una de estas dos formas: despempaquetando
un documento XML o ejemplarizando las clases generadas.
Desempaquetar
Una vez que hayamos generado las clases del DTD que especifica un documento XML, podemos desempaquetar el documento
en un árbol de contenido. En el directorio /examples/checkbook, se encuentra el fichero
llamado march.xml, que contiene las transacciones escritas en el
mes de marzo. Para desempaquetar este documento XML en un árbol de contenido en el fichero
CheckbookApp.java:
- Creamos un metodo llamado buildTrees:
public static void buildTrees() throws Exception {
}
- En nuestro nuevo método leemos el fichero XML en un FileInputStream:
File march = new File("march.xml");
FileInputStream fIn = new FileInputStream(march);
- Llamamos al método unmarshal de Transactions,
que es la clase que representa el elemento raíz transactions del
checkbook.dtd:
try {
marchTrans = marchTrans.unmarshal(fIn);
}
finally {
fIn.close();
}
- Llamamos al método buildTrees desde nuestro método main:
buildTrees();
En este punto, CheckbookApp genera un árbol de contenido desde march.xml.
La sección Acceder al Contenido mostrará cómo manipular el contenido
del árbol. La sección siguiente muestra cómo construir un árbol de contenido ejemplarizando las clases generadas.
Ejemplarización
Si tenemos un DTD XML pero ningun ejemplar de documento XML válido especificado por el DTD, podemos crear un documento
XML válido construyendo un árbol de contenido desde las clases derivadas y empaquetando el árbol en un documento XML.
Supongamos que deseamos crear un árbol de contenido que representa una lista de transacciones para el mes de abril.
Para construir este árbol de contenido con ejemplarización:
- En el método buildTrees, después de la llamada para desempaquetar el fichero
march.xml, obtenemos la lista de entradas del objeto aprilTrans,
creamos un nuevo objeto Check, representando el cheque para el alquiler del mes de abril:
List aprilEntries = aprilTrans.getEntries();
Check aprilRentCheck = new Check();
CheckCategory aprilRent = CheckCategory.RENT;
aprilRentCheck.setCategory(aprilRent);
- Seleccionamos el nombre de la entidad que recibe el cheque:
aprilRentCheck.setName(Gilchrest Gardens Manor);
- Seleccionamos el número de cheque:
aprilRentCheck.setNumber(51);
Podemos pasar un int al método setNumber porque especificamos
en el esquema de unión que la propiedad number acepta y devuelve un
int.
- Seleccionamos la fecha para el cheque:
aprilRentCheck.setDate(TransDate.parseDate(04-12-2001));
Usamos el método parseDate de la clase TransDate que
proporcionamos en el Capítulo anterior porque especificamos nuestro
propio formato de fechas, que es:MM-dd-yyyy.
- Seleccionamos la cuantía del cheque:
aprilRentCheck.setAmount(new java.math.BigDecimal(1500.00));
Podemos pasar un java.math.BigDecimal al método setAmount
porque especificamos en el esquema de unión que la propiedad amout acepta y devuelve un
java.math.BigDecimal. Cuando comencemos a calcular el balance para el libro de cheques
en el siguiente capítulo, veremos la ventaja de realizar estas conversiones de tipos.
- Seleccionamos el estado del cheque a pendiente:
Pending pending = new Pending();
aprilRentCheck.setPendVoidClrd(pending);
- añadimos el cheque a la lista de entradas del árbol de contenidos aprilTrans:
aprilEntries.add(aprilRentCheck);
El objeto Entry representa una lista de transacciones, que incluye cualquier número de depósitos,
de cheques, y de reintegros. Las clases Deposit, Check, y
Witdrawal implementan Entry, que representa las funciones comunes de
estas tres clases. Después de que creemos un cheque, un depósito, o un reintegro, lo agregamos a la lista de entradas. Puesto que
la lista es modificable, las transacciones que le agregamos se añaden automáticamente al árbol de contenido.
Ahora tenemos dos árboles de contenido: uno para las transaciones de Marzo, y otro para las transaciones de Abril. La
siguiente sección explica como acceder al contenido de estos árboles.
Acceder al Contenido
Tanto si construimos un árbol de contenido desempaquetando un documento de XML o ejemplarizando sus clases, tenemos
acceso al contenido de la misma forma. Esta sección demostrará el acceso al contenido de los árboles de contenidos que
creamos en la sección anterior. En el fichero march.xml, tenemos un cheque de la tienda de
comestibles para "Conglomerate Fodds". Ahora nos hemos dado cuenta de que compramos en "Mom and Pop Foods" en su lugar.
Necesitamos cambiar el nombre del receptor del cheque de la tienda de comestibles a "Mom and Pop Foods"
- Creamos un nuevo método llamado accessContent:
public static void accessContent() {}
- En nuestro método accessContent, llamamos a getEntries
sobre el objeto marchTrans:
List entryList = marchTrans.getEntries();
El entryList contiene todas las transaciones contenidas en el árbol de contenido que
representa los datos de march.xml.
- Iteramos a través de la lista para encontrar las transaciones check:
for(ListIterator i = entryList.listIterator(); i.hasNext(); ) {
Entry entry = (Entry)i.next();
if ( entry instanceof Check ){
- Obtenemos la categoría e cada entrada check que encontramos para determinar
si el cheque de la categoría groceries:
CheckCategory category = ((Check) entry).getCategory();
if(category.equals(CheckCategory.GROCERIES)){
- Si el cheque pertenece a la catergoría groceries, seleccionamos el nombre del
receptor a "Mom and Pop Foods" y añadimos los corchetes de cierre:
((Check)entry).setName(Mom and Pop Foods);
break;
}
}
}
- Llamamos al método accessContent desde el método main:
accessContent();
Después de haber creado el árbol de contenido para las transacciones de Abril, nuestro casero nos informa de que ha aumentado
el alquiler a $2000. Así pues, necesitamos cambiar la cantidad del cheque del alquiler en el árbol de contenido que representa
las transacciones para el mes de Abril.
Para cambiar la cuantía del cheque del alquiler:
- En el método accessContent, obtenemos la lista de entrada, pero esta vez llamamos a
getEntries sobre el objeto aprilTrans:
List aprilEntries = aprilTrans.getEntries();
- Iteramos sobre la lista para encontrar el cheque del alquiler, y seleccionamos su cuantía a $2000:
for(ListIterator i = aprilEntries.listIterator(); i.hasNext(); ) {
Entry entry = (Entry)i.next();
if ( entry instanceof Check ){
CheckCategory category = ((Check) entry).getCategory();
if(category.equals(CheckCategory.RENT)){
entry.setAmount(new java.math.BigDecimal(2000.00));
}
}
}
Observa en el último paso que no hemos tenido que forzar Entry a un
Check para fijar la cuantía del cheque. Esto es porque especificamos en el
esquema de unión que la cantidad es uno de los miembros del interface Entry
porque es un elemento que comparten Check, deposit,
y Withdrawal. Así pues, cualquier ejemplar de
Entry tendrá una cuantía. En el paso 4 del primer conjunto de pasos, tuvimos que
forzar la entry a un Check para fijar el nombre
porque el nombre no es uno de los miembros del interface Entry. La razón por la
que no especificamos Entry para contener un nombre es porque
withdrawal no incluye name como uno de sus elementos.
Como hemos hecho cambios en los árboles de contenido, deberíamos asegurarnos de que nuestro árbol es válido antes de
empaquetarlo. La siguiente sección explica cómo validar los árboles de contenido.
Validación
Antes de empaquetar un árbol de contenido a un documento XML, debemos asegurarsnos de que el árbol de contenido
es válido con respecto al DTD. Si utilizamos desempaquetamiento en vez de ejemplarización para construir el árbol de
contenido, y no hemos modificado el árbol, no necesitamos validar antes de empaquetar porque el proceso de
desempaquetamiento incorpora la validación. Si utilizamos la ejemplarización para construir el árbol, siempre
necesitamos realizar explícitamente la validación antes de empaquetar. Hemos modificado ambos árboles de contenido en
la sección anterior, y así que debemos validarlos antes de empaquetarlos.
Para validad ámbos árboles de contenido:
- Creamos un método llamado validateTrees:
public static void validateTrees() throws Exception {}
- Dentro del método, llamamos a validate sobre marchTrans
y aprilTrans :
marchTrans.validate();
aprilTrans.validate();
- Llamamos a validateTrees desde el método main:
validateTrees();
Empaquetar
Después de validar los árboles de contenido, estamos listos para empaquetarlos en nuevos documentos XML. Tanto si construímos
un árbol de contenido usando desempaquetamiento o ejemplarización, empaquetamos un árbol de la misma forma. Para empaquetar
los árboles de contenido:
- Creamos un nuevo método llamado marshalTrees:
public static void marshalTrees() throws Exception {}
- En el nuevo método, creamos nuevos ficheros, para contener los contenidos actualizados de ámbos árboles:
File march_new = new File(march_new.xml);
File april_new = new File(april_new.xml);
- Creamos el obejto OutputStream para enviarlo al método marshal:
FileOutputStream fMOut = new FileOutputStream(march_new);
FileOutputStream fAOut = new FileOutputStream(april_new);
- Llamamos al método marshal sobre cada árbol:
try {
marchTrans.marshal(fMOut);
aprilTrans.marshal(fAOut);
}
finally {
fMOut.close();
fAOut.close();
}
- Llamamos a marshalTrees desde el método main:
marshalTrees();
Después de que recompilemos las clases y ejecutemos CheckbookApp, veremos los ficheros
march_new.xml y april_new.xml en el directorio. Si comparamos
march.xml con march_new.xml, encontraremos que la única diferencia entre
los dos ficheros es el nombre del cheque de la tienda de comestibles, que modificamos. JAXB
preserva la equivalencia entre un documento XML y el mismo documento XML formados desde su árbol de contenido.
La siguiente sección muestra cómo agregar el contenido del árbol que representa las transacciones de Abril al
árbol de contenido que representa las transacciones de Marzo.
Añadir Árboles de Contenido
Puesto que un objeto en un árbol de contenido puede tener más de un padre, podemos añadir árboles de contenido juntos.
Podemos emplear esta técnica para añadir las transacciones de Abril a las transacciones de Marzo antes de añadir las
transacciones al libro de cheques como se mostrará en el siguiente capítulo. Para añadir
AprilTrans a Trans:
- Creamos un nuevo método llamado appendTrees:
public static void appendTrees() {}
- En el nuevo método, obtenemos la lista de entradas de cada objeto Transactions:
List mEntries = marchTrans.getEntries();
List aEntries = aprilTrans.getEntries();
- Usamos el método addAll de List, para añadir la
lista completa de las transaciones de Abril a la lista de las transaciones de Marzo:
mEntries.addAll(aEntries);
- Llamamos a appendTrees desde el método main:
appendTrees();
El siguiente capítulo muestra cómo ampliar la clase Checkbook para proporcionar
funcionalidades para añadir nuestras transaciones de Marzo y Abril a nuestro libro de cheques y hacer el balance.