Parte I. INTRODUCCIÓN 1.1 ¿Qué es JAXB? El Lenguaje de Marcado Extensible (XML) y la tecnología Java son compañeros o socios de trabajo en la ayuda a los desarrolladores para intercambiar datos y programas a través de Internet. JAXB simplifica el acceso a un documento XML de un programa Java representando el documento XML en un programa en formato Java, esto es, provee a los desarrolladores de aplicaciones Java, una forma rápida y conveniente para enlazar o vincular esquemas XML a representaciones java. JAXB provee de métodos para desorganizar (unmarshal) documentos instancias XML en árboles de contenido (generados en código Java), para después utilizar los mismos y generar mediante el método organizar (marshal) instancias XML de las que fueron generados. Esto nos proporciona la flexibilidad de manejar datos XML en una plataforma “neutral” además de no requerir tratar o conocer las técnicas de programación de XML al ocultar ciertos detalles complejos de sus relaciones. JAXB proporciona potencia sin tener las desventajas de que el procesamiento de las aplicaciones sea pesado y/o complejo como ocurre con SAX o DOM. Las clases generadas JAXB describen solo relación real definida en los esquemas fuentes. El resultado de lo anterior son datos xml altamente portables que unido a un código java portable puede ser usado para crear flexibles y ligeras aplicaciones y servicios web. 1.2 Características de JAXB Al usar una aplicación JAXB obtenemos diversos beneficios, además contamos con una flexibilidad mucho mayor que otras aplicaciones nos dan, las características más importantes por las cuales nos conviene utilizar JAXB son las siguientes: 1 JAXB usa Tecnología Java y XML Garantiza Datos Válidos Es Rápida Es Fácil de Usar Puede Restringir Datos Es Personalizable Es Extensible Para comprender a detalle los beneficios de utilizar JAXB se da una explicación a continuación sobre cada una de estas características. 1.2.1 Las aplicaciones JAXB usan Tecnología Java y XML Una de las cualidades más importantes de las aplicaciones de JAXB es que están escritas en el lenguaje de programación Java y pueden procesar datos XML. Para poder entender las implicaciones de estas características es necesario primeramente comprender porque es tan importante la tecnología Java y el lenguaje XML, además de cómo es su implementación. El lenguaje XML es una forma, estándar industrial e independiente del sistema, de representar datos. Los datos que se representan usando XML se pueden publicar en múltiples medios porque XML describe la estructura de los datos, no su formato, al contrario que el HTML, Los datos de XML se pueden pasar entre aplicaciones porque la estructura de los datos se puede especificar en un esquema, lo que permite que un analizador de sintaxis valide y procese los datos que siguen el esquema. El lenguaje XML no proporciona etiquetas, como lo hace HTML, se hace uso de un esquema para definir nuestras propias etiquetas, y estas a su vez describen nuestros datos. Los datos XML son fáciles de trabajar porque están escritos en un formato de texto simple, legible por los seres humanos y el software de edición de texto. Por estas razones, XML se está convirtiendo rápidamente en un método común para el intercambio de datos entre aplicaciones, especialmente aplicaciones de empresa de negocio-a-negocio. Las aplicaciones escritas en el lenguaje de programación de Java son portables: ya que cualquier sistema con una máquina virtual Java puede ejecutar los bytecode producidos compilando una aplicación Java. Con el 2 código portable que la tecnología de Java proporciona, XML es aún más útil en el contexto de compartir datos entre las aplicaciones. Las aplicaciones, especialmente aplicaciones basadas en Web, necesitan la ayuda de la tecnología de Java para analizar y procesar los datos de una manera independiente de la plataforma. Asimismo, las aplicaciones Java necesitan el formato de datos independiente de la plataforma que XML proporciona para comunicar y compartir información. Podríamos decir que JAXB proporciona un puente entre estas dos tecnologías complementarias. Ya que JAXB incluye un compilador que asocia un esquema a un conjunto de clases Java. Una vez que tengamos nuestras clases, podremos construir las representaciones de objetos Java de los datos XML que siguen las reglas que el esquema define. Al igual que un documento XML es un ejemplar de un esquema, un objeto Java es un ejemplar de una clase. Así, JAXB permite que creemos los objetos Java en el mismo nivel conceptual que los datos XML. La representación de nuestros datos de esta manera permite que los manipulemos de manera semejante como manipularíamos objetos de Java, haciendo más fácil la creación de aplicaciones para procesar datos XML. Una vez que tengamos nuestros datos en la forma de objetos Java, es fácil acceder a ellos. Además, después de trabajar con los datos, podemos escribir los objetos Java en un nuevo documento XML. Con el acceso fácil a los datos XML que proporciona JAXB, solamente necesitamos escribir aplicaciones que realmente utilizarán los datos, en vez gastar el tiempo en escribir código para formatear los datos. 1.2.2 Las Aplicaciones JAXB son Rápidas Dos APIs de uso general para analizar XML son SAX (API simple para XML) y DOM (modelo del objeto del documento). Un analizador de sintaxis de SAX es un analizador de sintaxis dirigido por eventos, lo que significa que reacciona a los pedazos del documento mientras lo está analizando; no salva ninguna parte del documento en memoria. Un analizador de sintaxis de DOM construye una estructura de datos del documento en la memoria cuyo contenido puede ser manipulado, pero es mucho más lento que un analizador de sintaxis SAX. Una aplicación JAXB, por otra parte, tiene la velocidad de un analizador de sintaxis SAX y de la capacidad de almacenaje de datos de un analizador de sintaxis DOM. Aunque los analizadores de sintaxis SAX son rápidos, los primeros prototipos de JAXB han demostrado que JAXB puede ser más rápido que los analizadores de sintaxis SAX. JAXB hace más 3 rápidamente el análisis porque las clases generadas están precompiladas y contienen la lógica del esquema, de tal modo que evitan la interpretación dinámica que un analizador de sintaxis SAX debe realizar. Una aplicación JAXB puede construir una estructura de datos en memoria como un analizador de sintaxis DOM. Sin embargo, al contrario que DOM, no incluye muchas de las funciones adicionales para la manipulación del árbol. Al contrario que una aplicación DOM, una aplicación JAXB es específica de un esquema: No podemos utilizarla para procesar los documentos de XML que se basan en otro esquema. Por estas razones, una aplicación JAXB utiliza mucho más eficientemente la memoria que DOM. 1.2.3 Las Aplicaciones JAXB son Fáciles de Crear y de Usar Puesto que todo el código de proceso se genera por nosotros, JAXB es más fácil de utilizar que la mayoría de los analizadores de sintaxis XML: Con sólo un stream de entrada podemos tener acceso al contenido. Además, la mayoría de los analizadores de sintaxis XML se limitan al tipado de datos ofrecido por un DTD. Un DTD es un tipo de lenguaje de esquema de XML. Todavía necesitamos proporcionar al código de conversión, que puede ser propenso a errores y difícil de mantener. JAXB genera automáticamente el código que podemos personalizar para que realice la conversión de contenidos por nosotros. Si sabemos cómo programar en el lenguaje Java y tenemos un mínimo conocimiento de XML, podremos empezar a usar JAXB. Además, como las clases generadas cumplen las convenciones del API Java, es incluso más fácil empezar a trabajar con JAXB. 1.2.4 Las Aplicaciones JAXB Pueden Convertir Datos Aunque un documento de XML está especificado para un esquema, en este momento, un esquema está limitado en cómo puede especificar firmemente el contenido de un documento de XML. Las aplicaciones de intercambio de datos necesitan tipado de datos formal. XML 1,0 no proporciona explícitamente a tipado de datos más allá de expresar tipos como valores del atributo; estos valores del atributo deben entonces ser interpretados analizando el código proporcionado. Es decir podemos incorporar cualquier tipo de datos que deseemos entre dos etiquetas, tales como números enteros o cadenas, 4 mientras la estructura del documento esté conforme con la especificación del DTD. Pero lo que desearemos con frecuencia es poder convertir los datos, por ejemplo, para especificar que solamente un número entero se puede contener entre dos etiquetas <quantity>. JAXB proporciona capacidades tanto para la estructura como para la validación del contenido en el código generado, que podemos personalizar. Más importante, puesto que JAXB genera el código Java, podemos asignar tipos exclusivos del lenguaje de programación Java, tal como Date o BigDecimal, a nuestros elementos. 1.2.5 Las Aplicaciones JAXB Pueden Personalizarse Antes de generar las clases Java de desde nuestro DTD, escribimos lo que se llama un esquema de unión, que contiene instrucciones de cómo generar las clases. El esquema de unión se escribe en un lenguaje de unión basado en XML, cuyas construcciones utilizamos para escribir al esquema de unión de modo que podamos especificar cómo se generan las clases. Una de las personalizaciones más útiles son las conversiones de tipos de datos. Por ejemplo, como se mencionó en secciones anteriores, podemos especificar en el esquema de unión que el elemento quantity sólo debe contener números enteros. Además de las conversiones de tipos de datos, podemos utilizar el esquema de unión para controlar los nombres de las clases, los paquetes, y los tipos; y podemos generar constructores, interfaces, y enumeraciones personalizadas. El esquema de unión también permite que manejemos la evolución del esquema. Si nos anticipamos la cambio de nuestro esquema, el esquema de unión proporcionará constructores especiales que definen uniones flojas que permitan más flexibilidad. Cuando el esquema se desarrolle, todo lo que necesitamos hacer es editar el esquema de unión y ejecutar el compilador del esquema otra vez para crear las clases que reflejan los cambios. Si intentáramos cambiar las clases en lugar del esquema, una vez que ejecutáramos el compilador del esquema otra vez, los cambios serían sobrescritos. Porque las instrucciones de unión se especifican en el esquema de unión a parte del esquema y el código cuando se desarrolle el esquema, lo tendremos mucho más fácil para mantener la aplicación. 5 1.2.6 Las Aplicaciones JAXB son Extensibles Una vez que hayamos generado las clases Java, podremos utilizarlas sin modificaciones, o subclasificarlas para proporcionar funcionalidades adicionales. Los desarrolladores de JAXB diseñaron el proceso de unión para hacer que la derivación de subclases sea sencillo. 1.3 Arquitectura de JAXB Como se muestra en la figura anterior, una implementación JAXB incluye los siguientes componentes principales: Componente Descripción Esquema XML Un Esquema XML usa la sintaxis XML para describir las relaciones entre los elementos, atributos y entidades en un documento XML. El esquema XML define un patrón de documentos XML al que deben adherirse con una estructura definida por reglas y restricciones de datos especificadas en el esquema. Por default, el compilador vinculante JAXB une las clases java y los paquetes a un esquema fuente XML basado en las reglas definidas en la especificación JAXB en lo que se conoce como un Personalizaciones esquema de unión o vinculación. Puede ser que algunas veces las reglas por default de vinculación no sean suficientes para lo que Binding necesitas. JAXB soporta personalizaciones y sobrescribe las reglas de vinculación por default por el las personalizaciones de vinculación. 6 Compilador vinculante El compilador vinculante JAXB es el núcleo del modelo de procesamiento JAXB. Su función es transformar, o vincular, un esquema XML fuente a un conjunto de clases contenido JAXB en el lenguaje de programación Java. Básicamente, corres el compilador JAXB usando un esquema XML (opcionalmente con declaraciones de vinculación) como entrada, y el compilador vinculante genera clases java con las restricciones en el esquema fuente XML. Implementación de javax.xml.bind La implementación del framework de JAXB vinculante es una API de tiempo de ejecución que provee de interfaces para desorganizar, organizar y validar contenido XML en una aplicación Java. El framework vinculante comprende las interfaces definidas en el paquete javax.xml.bind. Clases derivadas del esquema Aplicación java Documento de entrada XML Salidas de documentos xml Estas son clases derivadas del esquema generadas por el compilador JAXB. Las clases específicas variaran dependiendo del esquema de entrada. En el contexto de JAXB, una aplicación java es una aplicación cliente que usa el marco para desorganizar datos XML, validar y modificar el contenido de objetos java, y organizar el contenido de regreso a datos XML. Típicamente, el marco de JAXB es envuelto en una aplicación java grande que puede proveer características UI, funciones de transformación XML, datos procesados, o lo que sea que sea deseado El contenido que es desorganizado como entrada para el framework JAXB, eso es, una instancia documento XML, de la cual es generada una representación java en forma de contenido de árbol. En la practica, el termino documento puede no tener el significado convencional, como una instancia documento XML, puede en su lugar, tomar la forma de flujos de datos pasados entre aplicaciones, o conjuntos de campos de bases de datos, o de infosets XML. En JAXB, el proceso de desorganización soporta la validación de una entrada documento XML contra las restricciones definidas en el esquema fuente. Este proceso de validación es opcional. El contenido XML es organizado fuera de un documento XML. En JAXB, organizar incluye parsear un objeto árbol de XML y escribirlo en un documento XML que es una representación precisa del documento original XML, y es válido con respecto al esquema fuente. JAXB puede organizar datos XML en documentos XML, manejadores de contenido SAX, y nodos DOM 7 1.4 El proceso de vinculación de JAXB La siguiente figura muestra lo que ocurre durante el proceso de vinculación JAXB Típicamente para generar una aplicación JAXB, hay una fase de desarrollo de aplicación en la cual las clases JAXB son generadas y compiladas, y una implementación vinculante es construida, seguida por una fase de utilización en la cual las clases JAXB generadas son usadas para procesar contenido XML en una continuo “vivir” en un escenario de producción. 1. Vincular el esquema. El primer paso del proceso es enlazar el esquema XML fuente en un conjunto de clases Java que representen ese esquema. Para realizar lo anterior un esquema XML es usado como entrada para el compilador vinculante de JAXB para generar clases JAXB que lo representen. Todas los proveedores de JAXB proveen una herramienta llamada compilador enlazante para vincular un esquema (la manera en que el compilador es invocado puede ser específica de la implementación). Ya que las clases son una implementación específica, las clases generadas por otro compilador enlazante en una implementación JAXB probablemente no funcionarán con otra implementación JAXB. Por lo tanto si cambias a otra implementación JAXB, deberías revincular el esquema con el compilador enlazante suministrado por esa aplicación. 8 Compilar las clases generadas. Todas las clases, interfaces, archivos fuentes, y código generado debe ser compilado. 2. Crear la aplicación JAXB que utilice las clases y demás archivos generados por el compilador enlazante y compilarla. Una vez hecho lo anterior, estas listo para convertir documentos xml a clases java o viceversa; esto lo podemos realizar mediante: Desorganizar y generación de árbol de contenido. Los documentos XML escritos de acuerdo a las restricciones en el esquema fuente son desorganizados por el framework vinculante JAXB. Note que JAXB también soporta desorganización de datos de otras fuentes que archivos/documentos, tales como nodos DOM, buffers de cadenas, fuentes SAX, y cosas así. Generar el árbol de contenido. El proceso de desorganización genera un árbol de contenido de objetos de datos instanciados de la clase generada JAXB; este árbol de contenido representa la estructura y contenido de los documentos fuentes XML. Validación (Opcional). El proceso de desorganización opcionalmente incluye la validación de los documentos fuentes XML antes de generar el árbol de contenido. Si modifica el árbol de contenido puede también usar la operación de validar JAXB para validar los cambios antes de organizar el contenido al documento XML que previamente se había desorganizado. Procesos de contenido. La aplicación cliente puede modificar los datos XML representados por el árbol de contenido Java por el uso de interfaces generadas por el compilador vinculante. Marshalling que es la operación contraria a unmarshalling, con el cual conviertes objetos Java en una representación documento XML. 9 1.5 Framework vinculante JAXB El framework vinculante de JAXB es implementado en tres paquetes java: El paquete javax.xml.bind, contiene clases e interfaces para la realización de operaciones tales como desorganización, organización y validación. El paquete javax.xml.bind.util contiene clases de utilidad que pueden ser usadas por una aplicación cliente para manejar los eventos de organización, desorganización y validación. El paquete de javax.xml.bind.helper provee una implementación parcial por default para algunas de las interfaces javax.xml.bind. Las implementaciones JAXB pueden extender estas clases e implementar los métodos abstractos. Este paquete está designado para los proveedores de implementaciones JAXB. 1.5.1 El paquete javax.xml.bind El paquete javax.xml.bind es el paquete principal del framework que define clases abstractas e interfaces que son usadas directamente con clases contenido. Define las clases Unmarshaller, Validator, y Marshaller, los cuales son objetos auxiliares para proveer sus operaciones respectivas. Además, define una herencia de eventos de validación y clases de excepciones para usar cuando ocurren errores de organización o desorganización. Las tres funciones principales proveídas por este paquete son organización, desorganización, y validación. Define también la clase JAXBContent la cual es el punto de entrada para una aplicación java dentro del marco de JAXB. La clase JAXBContext provee una 10 abstracción para administrar la información de vinculación XML/JAVA necesaria para implementar las operaciones de desorganizar, organizar y validar. Una aplicación cliente obtiene nuevas instancias de esta clase por el uso del método newInstance(contextPath); por ejemplo: JAXBContext jc= JAXBContext.newInstance(“com.acme.foo:com.acme.bar”); El parámetro contexPath contiene una lista de los nombres de paquetes java que contienen las interfaces derivadas de un esquema—particularmente las interfaces generadas por el compilador enlazante JAXB. El valor de este parámetro inicializa el objeto JAXBContext para permitir la manipulación de las interfaces derivadas del esquema. Para este termino, la implementación proveedora de JAXB debe suministrar una clase implementación conteniendo un método con la siguiente declaración: Public static JAXBContext createContext(String contextPath, ClassLoader classLoader) Throws JAXBException; 1.5.1.1 Desorganización La clase Unmarshaller en el paquete javax.xml.bind suministra a la aplicación cliente la habilidad para convertir datos XML en un árbol de contenido de objetos Java. El método unmarshal para un esquema (dentro de un nombre de espacios) permite para cualquier elemento global XML declarado en el esquema para ser desorganizado como la raíz de un documento instancia. Una aplicación cliente es capaz de desorganizar documentos XML que son instancias de cualquiera de los esquemas listados en la ContextPath; por ejemplo: JAXBContext jc = JAXBContext.newInstance("com.acme.foo:com.acme.bar"); Unmarshaller u = jc.createUnmarshaller(); FooObject fooObj =(FooObject)u.unmarshal( new File( "foo.xml" )); // ok BarObject barObj =(BarObject)u.unmarshal( new File( "bar.xml" )); // ok BazObject bazObj =(BazObject)u.unmarshal( new File( "baz.xml" )); // error, "com.acme.baz" not in contextPath Una aplicación cliente puede también generar árboles de contenido explícitamente en lugar de desorganizar datos XML existentes. 11 ObjectFactory objFactory = new ObjectFactory(); com.acme.foo.PurchaseOrder po = objFactory.createPurchaseOrder(); Una vez que la aplicación cliente tiene una instancia de un objeto derivado del esquema, este puede usar los métodos el transformador de métodos para establecer contenido en este punto 1.5.1.2 La Organización La clase Marchaller dentro del paquete jabax.xml.bind provee a la aplicación cliente la habilidad para convertir una árbol de contenido java de regreso a datos XML. No hay diferencia entre organizar un árbol de contenido que es creado manualmente usando los métodos de fábrica y organizar a un árbol de contenido que es el resultado de una operación de desorganización. El proceso de organización puede alternativamente producir flujos de eventos SAX2 para un registrado ContentHandler o producir un Nodo objeto DOM. Un simple ejemplo que desorganiza un documento xml y luego lo organiza de regreso fuera es como sigue JAXBContext jc = JAXBContext.newInstance( "com.acme.foo" ); // unmarshal from foo.xml Unmarshaller u = jc.createUnmarshaller(); FooObject fooObj = (FooObject)u.unmarshal( new File( "foo.xml" ) ); // marshal to System.out Marshaller m = jc.createMarshaller(); m.marshal( fooObj, System.out ); 1.5.1.3 La Validación La clase Validador en el paquete javax.xml.bind es responsable del control de la validación del árbol contenido durante el tiempo de ejecución. Cuando el proceso de desorganización incorpora validación y esta fue completada exitosamente sin algún error de validación, ambos el documento de entrada y el árbol de contenido resultante están garantizados de ser validos. Por el contrario, el proceso de organización en realidad no realiza validación. 12 En general, si una implementación JAXB no puede inequívocamente completar la desorganización u organización, esta terminara el procesamiento con una excepción. Un cliente JAXB pude realizar dos tipos de validación: Validación al tiempo de desorganizar permite a una aplicación cliente recibir información acerca de los errores y advertencias detectadas durante la desorganización de datos XML dentro de un árbol de contenido, y es completamente ortogonal a los otros tipos de validación. Para habilitar o deshabilitar esto usa el método Unmarshaller.setValidating. Todos los proveedores JAXB son requeridos para soportar esta operación. Validación por demanda permite a una aplicación cliente recibir información acerca de errores de validación y advertencias detectadas en el árbol de contenido. A cualquier punto, las aplicaciones cliente pueden llamar al método Validator.validate en el árbol de contenido (o cualquier subárbol de este). Todos los proveedores están obligados a soportar esta operación 1.6 Versiones Como es natural, JAXB ha tenido hasta la fecha varias versiones, que corresponden a características o soportes nuevos entre cada versión. El lenguaje de esquema W3C no es el único lenguaje de esquema, sino que es el único al que se ha hecho referencia aquí. Sin embargo, la especificación XML describe DTD como la forma de expresar un esquema. Versiones preliberadas de implementaciones JAXB solo trabajaban con DTD’s. Sin embargo en la actualidad, en las últimas versiones de JAXB, se da soporte también al esquema XML. El esquema XML permite mayores potencialidades que los DTD como se verá un poco mas adelante. 13 Parte II. DOCUMENTOS XML 2.1 ¿Qué es XML? XML, sigla en inglés de eXtensible Markup Language (Lenguaje de Marcas Extensible), es un metalenguaje extensible de etiquetas desarrollado por el World Wide Web Consortium (W3C). XML es clasificado como un lenguaje extensible debido a que permite a sus usuarios definir sus propias etiquetas. Es una simplificación y adaptación del lenguaje SGML y permite definir la gramática de lenguajes específicos (de la misma manera que HTML es a su vez un lenguaje definido por SGML). Por lo tanto XML no es realmente un lenguaje en particular, sino una manera de definir lenguajes para diferentes necesidades. Algunos de estos lenguajes que usan XML para su definición son XHTML, SVG, MathML. XML no ha nacido sólo para su aplicación en Internet, sino que se propone como un estándar para el intercambio de información estructurada entre diferentes plataformas. Se puede usar en bases de datos, editores de texto, hojas de cálculo y casi cualquier cosa imaginable. XML es una tecnología sencilla que tiene a su alrededor otras que la complementan y la hacen mucho más grande y con unas posibilidades mucho mayores. Tiene un papel muy importante en la actualidad ya que permite la compatibilidad entre sistemas para compartir la información de una manera segura, fiable y fácil. XML fue desarrollado por el Grupo de Trabajo XML (XML Working Group), originalmente conocido como SMGL Editorial Review Borrad), formado bajo el auspicio de la W3C en 1996. Las metas diseñadas para XML son: XML debe buscar la usabilidad sobre Internet. XML debe soportar una amplia variedad de aplicaciones. XML debe ser compatible con SGML. Debe ser fácil escribir programas que procesen documentos XML. 14 El número de características opcionales en XML debe mantenerse al mínimo absoluto, idealmente ninguno. Los documentos XML deben ser legibles para personas y ser razonablemente claros. El diseño XML debe ser preparado rápidamente. El diseño de XML debe ser formal y conciso. Los documentos XML deben ser fáciles de crear. La brevedad en el etiquetado XML es de mínima importancia. 2.2 Definición de Tipo de Documento (DTD) Un DTD modela un documento: Definiendo un conjunto de elementos que pueden aparecer en un documento XML. Definiendo el modelo del contenido de cada elemento. Un modelo del contenido describe lo que un elemento puede contener en términos de cualquier subelementos y datos. Definiendo un conjunto de atributos opcionales u obligatorios para cada elemento. La definición de atributos incluye el nombre del atributo, el valor por defecto y el tipo de dato. Como el resto de un documento XML, un DTD es simplemente una sección especial de texto. Sin embargo, debe aparecer al principio de un documento XML para que una aplicación que reconozca XML pueda validar el documento. Un DTD puede ser externo o interno a un documento XML. Un documento DTD externo reside en otro archivo, a veces en otra computadora. Un DTD interno reside en el mismo archivo que el documento XML. Esto es conocido algunas veces como DTD privado porque define el modelo de datos que un documento XML específico utiliza. En contraste, un DTD externo usualmente define un modelo de datos para un conjunto de documentos XML relacionados. Como resultado, un DTD externo a veces es referido como DTD público o compartido. Ya sea interno o externo, un DTD siempre aparece al inicio de un documento XML, inmediatamente después de la declaración de XML. 15 2.2.1 Declaración de Tipo de Documento Un DTD inicia con la declaración del tipo de documento, que contiene una referencia a un DTD externo o declara un DTD interno. La declaración de tipo de documento inicia con <!DOCTYPE, que debe estar todo en mayúsculas como se muestra en número 1. Si planea declarar un DTD interno, debe proveer el nombre del elemento raíz como se muestra en el número 2. Como se mencionó previamente, un DTD externo a veces es llamado como público o compartido porque reside en un archivo separado. Sin embargo, puede declarar un DTD externo como privado usando la declaración del número 3. Puede declarar un DTD compartido y públicamente accesible usando la sintaxis mostrada en el número 4. El número 5 representa una declaración DTD interna (lo de “…declaration…” no es parte del DTD). Este tipo de declaración es para el uso exclusivo del documento XML en el que la declaración aparece. DTDs internos son convenientes porque son fáciles de mantener y transportar, pero no son considerados para ser reutilizables. Aquí hay algunos ejemplos de varias declaraciones de tipo de documento. <!DOCTYPE people> <!DOCTYPE people SYSTEM “…/people.dtd”> 16 <!DOCTYPE book PUBLIC “-//OASIS//DTD DocBook XML V4.1.2//EN” http://www.oasis.open.org/docbook/xml/4.0/docbook.dtd> <!DOCTYPE employees SYSTEM “employees.dtd” […]> 2.2.2 Declaración de Elementos La declaración de un elemento inicia con el delimitador <! (número 1 en la figura) inmediatamente seguido por ELEMENT en mayúsculas (número 2 en la figura). El nombre del elemento (número 3) especifica qué nombre tendrá el elemento. El modelo de contenido (número 4) le permite declarar los cuatro tipos de elementos que puede crear usando XML: Elementos vacíos (no contienen datos) Elementos con sólo elementos (contiene solamente otros elementos) Elementos mezclados (contiene texto y otros elementos) Elementos any (pueden contener cualquier contenido permitido) 2.2.2.1 Elementos vacíos Los elementos vacíos no contienen ningún contenido y típicamente usan atributos para llevar datos e información. El siguiente ejemplo declara un elemento vacío: <!ELEMENT lineBreak EMPTY> Así es como el elemento previo puede aparecer como parte de un documento XML: <lineBreak/> <!—versión corta 1 --> 17 <lineBreak /> <!-- versión corta 2 --> <lineBreak></lineBreak> <!-- versión larga --> Esto muestra dos notaciones abreviadas. Ambas usan la secuencia de caracteres />, pero la diferencia es el espacio entre el nombre del elemento y la secuencia de caracteres de cierre. La versión larga no es deseable a comparación de las dos primeras formas. Las primeras dos dejan más claro que el elemento está vacío mientras que la versión larga no. 2.2.2.2 Elementos con sólo elementos Elementos con sólo elementos no contienen datos pero contienen otros elementos. La declaración de un elemento con sólo elemento describe el nombre del elemento junto con el contenido que puede contener. La forma general de la declaración de un elemento con sólo elementos es <!ELEMENT nombre contenido> El nombre representa el nombre del elemento como aparece en el documento XML, y el contenido representa lo que el elemento puede contener. Una declaración de sólo elementos puede contener Una lista ordenada de elementos Un elemento de una lista de elementos válidos Un conjunto repetido de elementos Una mezcla de todas las anteriores La lista ordenada de elementos es la más sencilla de entender de los tres tipos de contenidos. Asumiendo que está modelando un documento XML que contiene información acerca de algunos libros, y quiere asegurarse de que contenga información básica acerca de cada libro, como la editorial, fecha de publicación, ISBN, título, subtítulo, autor y un comentario, en ese orden. Puede expresar una lista ordenada de elementos usando la siguiente sintaxis: <!ELEMENT nombreElemento(elemento1,elemento2,elemento3,...,elementoN)> La declaración especifica el nombre del elemento, el cuál contiene los subelementos ordenados. Las comas indican que el listado es una lista ordenada. Como resultado, los subelementos deben aparecer en el orden especificado en el listado entre paréntesis. 18 Con el ejemplo de los libros quedaría así: <!DOCTYPE libros [ <!ELEMENT libros (libro)> <!ELEMENT libro (editorial, fechaLanzamiento, isbn, titulo, subtitulo, autor, comentario)> … ]> No muestra el DTD completo, porque lo que ve aquí está incompleto porque el DTD no especifica qué contenido tienen los elementos autor, isbn y los otros. Asumiendo que los subelementos no contienen ningún dato (son elementos vacíos), el DTD completo se verá como esto: <!ELEMENT libros (editorial, fechaLanzamiento, isbn, titulo, subtitulo, autor, comentario)> <!ELEMENT editorial EMPTY> <!ELEMENT fechaLanzamiento EMPTY> <!ELEMENT isbn EMPTY> <!ELEMENT titulo EMPTY> <!ELEMENT subtitulo EMPTY> <!ELEMENT autor EMPTY> <!ELEMENT comentario EMPTY> El siguiente documento sería válido usando el DTD previo: <?xml version=”1.0”?> <libros> <editorial/> <fechaLanzamiento/> <isbn/> <titulo/> <subtitulo/> <autor/> <comentario/> </libros> Si quiere incluir el DTD junto con el documento XML, quedaría así: <?xml version=”1.0”?> <!DOCTYPE libros [ <!ELEMENT libros (editorial, fechaLanzamiento, isbn, titulo, subtitulo, autor, comentario)> <!ELEMENT editorial EMPTY> <!ELEMENT fechaLanzamiento EMPTY> 19 <!ELEMENT isbn EMPTY> <!ELEMENT titulo EMPTY> <!ELEMENT subtitulo EMPTY> <!ELEMENT autor EMPTY> <!ELEMENT comentario EMPTY> ]> <libros> <editorial/> <fechaLanzamiento/> <isbn/> <titulo/> <subtitulo/> <autor/> <comentario/> </libros> En contraste con una lista ordenada de elementos, una selección de elemento lista una serie de elementos que un elemento puede contener. Sin embargo, el elemento puede contener sólo uno de los elementos en la lista. Supongamos que está modelando un documento de inventario que contiene el precio de venta y el precio de compra, pero no quiere que los dos precios aparezcan para un ítem dado. Quiere que aparezca un precio, pero sólo el precio de venta o el precio de compra. El siguiente DTD modela el documento del inventario: <!ELEMENT ítems (item+)> <!ELEMENT item (precioVenta | precioCompra)> <!ELEMENT precioVenta EMPTY> <!ELEMENT precioCompra EMPTY> Si queremos incluir el DTD al inicio del documento XML, debemos ponerlo con la declaración DOCTYPE: <?xml version=”1.0”?> <!DOCTYPE ítems [ <!ELEMENT items (item+)> <!ELEMENT item (precioVenta | precioCompra)> <!ELEMENT precioVenta EMPTY> <!ELEMENT precioCompra EMPTY> ]> <items> <item> <precioVenta/> </item> <item> <precioCompra/> </item> 20 <item> <precioVenta/> </item> </items> Esto introduce un conjunto de elementos repetidos. Aquí hay un vistazo más cercano del elemento que lo declara: <!ELEMENT ítems (item+)> La declaración del elemento se ve muy similar a una lista ordenada que contiene sólo un elemento, excepto que un signo más sigue a la palabra item. Esto indica que el nombre del elemento que lo precede debe aparecer al menos una vez y puede aparecer cualquier número de veces. Puede usar varios símbolos para restringir el número de veces que los subelementos pueden aparecer, como se muestra en la siguiente tabla: Tabla de símbolos en declaraciones de elementos (sin (sin símbolo) símbolo) ? Signo de interrogación * Asterisco + Símbolo más () Paréntesis | Pipe , Coma Indica que el elemento debe aparecer exactamente una sola vez. Indica que el elemento es opcional, puede aparecer una vez o ninguna. Indica que el elemento puede aparecer cero o más veces. Indica que un elemento puede aparecer una o más veces. Agrupa una lista de secuencia o de elección. Usado como parte de una lista de opciones, puede seleccionar un ítem de la lista. Usada como parte de una secuencia, puede usar los elementos en la secuencia basado en sus restricciones individuales. Estos símbolos pueden ser útiles para que el DTD sea más complejo. Por ejemplo, los libros usualmente tienen una dedicatoria, prefacio, tabla de contenidos y uno o más capítulos. Cada capítulo en un libro tiene al menos un encabezado y un párrafo, el cuál puede contener texto, una figura, una tabla o una lista. En lugar de imponer esta estructura, puede agregar flexibilidad usando la siguiente declaración: <!ELEMENT libro ((dedicatoria?), prefacio, tdc, (capitulo+))> <!ELEMENT capitulo (encabezado, (parrafo+))> <!ELEMENT parrafo (#PCDATA | figura | tabla | lista)*> 21 Este fragmento de DTD indica que la dedicatoria es opcional, indicado por el símbolo ¿, pero que debe haber un prefacio, una tabla de contenidos (tdc), y al menos un capítulo. Cada capítulo tiene un encabezado y uno o más párrafos que pueden contener texto (#PCDATA), una figura, una tabla o una lista. Notemos que el elemento párrafo, además de los subelementos que contiene, también contiene texto. Este tipo de elemento es referido como elemento de contenido mixto. 2.2.2.3 Elementos de Contenido Mixto Un elemento mixto puede contener datos y elementos. Este modelo es una extensión de los elementos que contienen sólo elementos que también permite datos de texto. El siguiente código declara algunos elementos mixtos: <!DOCTYPE paises [ <!ELEMENT paises (pais)> <!ELEMENT pais (#PCDATA | ubicacion | poblacion)> <!ELEMENT poblacion (#PCDATA | unidades)> <!ELEMENT ubicacion (#PCDATA | region | #PCDATA)> <!ELEMENT comentario (#PCDATA)> ]> Esto declara un documento que contiene una colección de países. Cada país está hecho de texto (#PCDATA), y elementos de ubicación y población. #PCDATA puede aparecer más de una vez en la declaración de un elemento, como se mostró en el elemento ubicación. Puede declarar un elemento de sólo texto usando la forma mostrada en el elemento comentario. 2.2.2.4 Elementos ANY Un elemento ANY puede contener cualquier contenido. Declare el elemento así: <!ELEMENT nombre ANY> 22 El elemento ANY no tiene ninguna estructura. Como resultado, debe evitar usarlo en sus propios DTDs. Su principal rol es actuar como comodín hasta que decida qué tipo de elemento debe contener. 2.2.3 Declaración de Atributos Los atributos permiten añadir información adicional a los elementos de un documento. La principal diferencia entre los elementos y los atributos, es que los atributos no pueden contener subatributos. Se usan para añadir información corta, sencilla y desestructurada. <mensaje prioridad="urgente"> <de>Alfredo Reino</de> <a>Hans van Parijs</a> <texto idioma="holandés"> Hallo Hans, hoe gaat het? ... </texto> </mensaje> Otra diferencia entre los atributos y los elementos, es que cada uno de los atributos sólo se puede especificar una vez, y en cualquier orden. En el ejemplo anterior, para declarar la lista de atributo de los elementos <mensaje> y <texto> haríamos lo siguiente: <!ELEMENT mensaje (de, a, texto)> <!ATTLIST mensaje prioridad (normal | urgente) normal> <!ELEMENT texto (#PCDATA)> <!ATTLIST texto idioma CDATA #REQUIRED> Las declaraciones de los atributos empiezan con "<!ATTLIST", y a continuación del espacio en blanco viene el identificador del elemento al que se aplica el atributo. Después viene el nombre del atributo, su tipo y su valor por defecto. En el ejemplo anterior, el atributo "prioridad" puede estar en el elemento <mensaje> y puede tener el valor "normal" o "urgente", siendo "normal" el valor por defecto si no especificamos el atributo. El atributo "idioma", pertenece al atributo texto, y puede contener datos de carácter CDATA. Es más, la palabra #REQUIRED significa que no tiene valor por defecto, ya que es obligatoria especificar este atributo. 23 A menudo interesa que se pueda omitir un atributo, sin que se adopte automáticamente un valor por defecto. Para esto se usa la condición "#IMPLIED". Por ejemplo, en una supuesta DTD que define la etiqueta <IMG> de HTML: <!ATTLIST IMG URL CDATA #REQUIRED> <!ALT CDATE #IMPLIED> Es decir, el atributo "URL" es obligatorio, mientras que el "ALT" es opcional (y si se omite, no toma ningún elemento por defecto). 2.2.3.1 Atributos CDATA y NMTOKEN Los atributos CDATA (Character DATA) son los más sencillos, y pueden contener casi cualquier cosa. Los atributos NMTOKEN (NaMe TOKEN) son parecidos, pero sólo aceptan los caracteres válidos para nombrar cosas (letras, números, puntos, guiones, subrayados y los dos puntos). <!ATTLIST mensaje fecha CDATA #REQUIRED> <mensaje fecha="15 de Diciembre de 1999"> <!ATTLIST mensaje fecha NMTOKEN #REQUIRED> <mensaje fecha="15-12-1999"> 2.2.3.2 Atributos enumerados y notaciones Los atributos enumerados son aquellos que sólo pueden contener un valor de entre un número reducido de opciones. <!ATTLIST mensaje prioridad (normal | urgente) normal> Existe otro tipo de atributo parecido, llamado de notación (NOTATION). Este tipo de atributo permite al autor declarar que su valor se ajusta a una notación declarada. <!ATTLIST mensaje fecha NOTATION (ISO-DATE | EUROPEAN-DATE) #REQUIRED> Para declarar las notaciones, se utiliza "<!NOTATION" con una definición externa de la notación. La definición externa puede ser pública o un 24 identificador del sistema para la documentación de la notación, una especificación formal o un asistente de la aplicación que contenga objetos representados en la notación <!NOTATION HTML SYSTEM "http://www.w3.org/Markup"> <!NOTATION HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 2.2.3.3 Atributos ID e IDREF El tipo ID permite que un tipo determinado tenga un nombre único que podrá ser referenciado por un atributo de otro elemento que sea de tipo IDREF. Por ejemplo, para implementar un sencillo sistema de hipervínculos en un documento: <!ELEMENT enlace EMPTY> <!ATTLIST enlace destino IDREF #REQUIRED> <!ELEMENT capitulo (parrafo)*> <!ATTLIST capitulo referencia ID #IMPLIED> En este caso, una etiqueta <enlace destino="seccion-3"> haría referencia a un <capitulo referencia="seccion-3">, de forma que el procesador XML lo podría convertir en un hipervínculo, u otra cosa. 2.3 XML Schema XML Schema es un lenguaje de esquema escrito en XML, basado en la gramática y pensado para proporcionar una mayor potencia expresiva que la DTD, más limitado en la descripción de los documentos a nivel formal. Los documentos esquema (usualmente con extensión .xsd de XML Schema Definition (XSD)) se concibieron como una alternativa a las DTD, más compleja, intentando superar sus puntos débiles y buscar nuevas capacidades a la hora de definir estructuras para documentos XML. La principal aportación de XML Schema es el gran número de los tipos de datos que incorpora. De esta manera, XML Schema aumenta las posibilidades y funcionalidades de aplicaciones de procesado de datos, incluyendo tipos de datos complejos como fechas, números y strings. 25 "XML Schema" (Esquema XML) es el nombre oficial otorgado a la recomendación del W3C, que elaboró el primer lenguaje de esquema separado de XML (la definición de tipo de documentos (DTD) forma parte de XML). XML Schema supera muchas de las limitaciones y debilidades de las DTDs. Fue diseñado completamente alrededor de namespaces y soporta tipos de datos típicos de los lenguajes de programación, como también tipos personalizados simples y complejos. Un esquema se define pensando en su uso final. Los esquemas se construyen a partir de diferentes tipos de componentes: Elemento (element) Atributo (attribute) Tipo simple (simple type) Tipo complejo (complex type) Notación (notation) Grupo modelo nombrado (named model group) Grupo de atributos (attribute group) Restricción identidad (identity constraint) Estos componentes ofrecen la posibilidad de combinar características de alto o bajo nivel: Alto nivel: Se encargan de ofrecer un significado semántico del contenido del documento. Analizan el contenido y extraen de él un significado. Éste puede estar predefinido en la declaración del esquema o se puede extraer de la misma estructura. Bajo nivel: Son características más concretas del documento que están incluidos en los diferentes campos del esquema y se accede a ellas de manera directa. Son los que se comparan directamente con el criterio de búsqueda definido y halla palabras concretas en la definición de los esquemas. 2.3.1 Estructura Mínima de un Esquema <?xml version="1.0" encoding="ISO-8859-1"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="0.1" xml:lang="es"> 26 </xsd:schema> La programación en Schema XML se basa en Namespaces. Podemos encontrar una analogía entre éstos y los llamados packages en Java. Cada Namespace contiene elementos y atributos que están estrechamente relacionados con el Namespace. Así, a la hora de definir un elemento o un atributo de un Namespace, siempre se creará una conexión entre los diferentes campos de éste. Además, esta forma de trabajar nos permite relacionar elementos que no están en el mismo Namespace. Después de escribir un Schema XML se puede confirmar la correcta realización mediante la validación de esquemas XML: Validación XML. 2.3.2 Tipos de Datos Los tipos de datos en XML Schema pueden ser simples o complejos: Tipos simples: son aquellos que no tienen ni elementos hijos ni atributos. Son tipos simples: Tipos predefinidos de XML: string, double, boolean, etc. List (lista de datos separados por espacios). Union (tipo de dato derivado de la unión de tipos predefinidos). Tipos complejos: Son tipos complejos aquellos que tienen elementos hijos y/o atributos. Pueden tener nombre o ser anónimos. Si tienen nombre pueden ser reutilizados dentro del mismo XML Schema o por otros XML Schemas. Es posible "mezclar" o combinar elementos y texto. 27 2.3.3 Diagrama de los Tipos de Datos que manejan los Schemas 2.4 Documento XML Suponiendo que tuvieramos un documento xml de esta forma: <?xml version="1.0"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note> Para hacer referencia a un dtd escribimos: <?xml version="1.0"?> <!DOCTYPE note SYSTEM "http://www.w3schools.com/dtd/note.dtd"> <note> <to>Tove</to> <from>Jani</from> 28 <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note> Mientras que para hacer referencia a un schema escribimos: <?xml version="1.0"?> <note xmlns="http://www.w3schools.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com note.xsd"> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note> 2.4.1 Ejemplo: Diferencias entre DTD y Schemas Utilizando el documento xml anterior, podriamos definir sus elementos con un DTD de esta manera: <!ELEMENT note (to, from, heading, body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> Utilizando un schema nos quedaria de esta forma: <?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3schools.com" xmlns="http://www.w3schools.com" elementFormDefault="qualified"> <xs:element name="note"> <xs:complexType> <xs:sequence> <xs:element name="to" type="xs:string"/> <xs:element name="from" type="xs:string"/> <xs:element name="heading" type="xs:string"/> <xs:element name="body" type="xs:string"/> </xs:sequence> </xs:complexType> 29 </xs:element> </xs:schema> 30 Parte III. ACCESO A UN DOCUMENTO XML 3.1 Construcción de una aplicación JAXB Antes de construir una aplicación JAXB primeramente necesitamos tener un esquema XML. En la versión 1.0 de JAXB, se requiere que el esquema utilizado sea un DTD, según lo establecido en la especificación XML 1.0. No obstante en las versiones posteriores a la 1.0, podemos utilizar el Schema XML. Después de tener el Schema o DTD, seguimos los siguientes pasos para poder construir y usar nuestra aplicación JAXB. 1. Escribir el esquema de Unión: es un documento XML que contiene instrucciones de cómo unir un esquema a las clases. Estas indicaciones pueden ser por ejemplo, el tipo primitivo al que se debe unir un valor de atributo en la clase generada. 2. Generar los ficheros fuente de Java: para esto usamos el compilador de esquema, ya que este toma al schema o DTD y al esquema de unión como entrada de información. Cuando ya se haya compilado el código fuente, podemos escribir una aplicación basada en las clases que resulten. 3. Construir el árbol de objetos Java: con nuestra aplicación, construimos el árbol de objetos java, también llamado árbol de contenido, este representara los datos XML que son validos con el schema o DTD. Hay 2 maneras de hacer esto: Ejemplarizando las clases generadas. Invocando al método unmarshal de una clase generada y pasarlo en el documento. El Método unmarshal toma un documento XML válido y construye una representación de árbol de objetos. 4. Acceder al árbol de contenido usando nuestra aplicación: con nuestra aplicación podemos acceder al árbol de contenido y modificar sus datos. 31 5. Generar un documento XML desde el árbol de contenido: para poder hacerlo tenemos que invocar al método marshal sobre el objeto raíz del árbol. 3.2 Creación del Esquema de Unión Para unir un schema o 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 esquema de unión contiene las instrucciones para unir el DTD a las clases, y lo utilizamos para controlar el código que genera el compilador de esquema. Por esto es importante comprender como interpreta el compilador de esquema a las declaraciones que hacemos en el esquema de unión y lo que asume dicho compilador si no proporcionamos declaraciones de unión para una declaración del schema o DTD. Al escribir el esquema de unión, no es necesario que proporcionemos declaraciones particulares de unión para cada uno de los componentes del schema o DTD, ya que cuando las omitimos, el compilador de esquema asume declaraciones de uniones por defecto. Sin embargo, si no nos satisfacen las uniones por defecto, podemos escribir en el esquema de unión las declaraciones de unión que necesitamos para generar las clases deseadas. 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. Para crear el esquema de unión mínimo requerido seguimos los siguientes pasos: 1. Creamos un nuevo archivo de texto con extensión .xjs. 2. Para identificar al archivo como un esquema de unión, escribimos la etiqueta: <xml-java-binding-schema version=”1.0ea”> 3. Los esquemas de unión deben declarar al menos un elemento raíz. Para declararlo escribimos: 32 <element name=”nombre_elemento_raiz” type=”class” root=true/> Al declarar los elementos raíz, utilizamos la declaración de unión del elemento para unir un tipo de elemento a una clase. El valor del atributo name, debe ser el nombre del elemento tal como aparece en el schema o DTD. El valor del tipo de atributo class indica que los elementos raíz se limitan a las clases. Finalmente el valor del atributo root debe ser igual a true para que en la estructura de los documentos XML podamos declarar como elementos raíz al elemento o elementos que declaramos como tal en el esquema de unión. 4. Introducimos la etiqueta de cierre para el elemento xml-java-bindingschema : </xml-java-binding-schema> Ahora ya tenemos el esquema de unión desde el cual el compilador de esquema puede generar clases. Recordemos que el compilador de esquema hará declaraciones de uniones por defecto, para las otras declaraciones del schema o DTD a las que no les hayamos proporcionado una declaración de unión. 3.2.1 Declaración de Unión de Elementos El compilador de esquema asume las diversas declaraciones de unión para los elementos dependiendo del tipo de contenido que tienen y sus atributos si es que los hay. Para los elementos simples, es decir, los que no tienen atributos y solo tienen contenido de tipo carácter, el compilador de esquema asumirá que los elementos están unidos a las propiedades dentro de la clase de su elemento padre. Para esto el compilador generara el siguiente código de declaración de unión por defecto: <element name=”nombre_elemento_simple” type=”value”/> El nombre de los atributos debe ser el nombre del elemento tal y como aparece en el schema o DTD. El tipo del atributo es value porque estos elementos están unidos a propiedades y no a clases. Las propiedades que generará el compilador de esquema con estas declaraciones de unión serán: String getNombre_elemento_simple(); void setNombre_elemento_simple(String x); 33 Si los elementos contienen cualquier cosa distinta de contenido de tipo carácter o si tienen atributos, el compilador de esquema asumirá que están unidos a clases. Por lo que las uniones por defecto de estos elementos serán: <element name=”nombre_elemento” type=”class”> Podemos observar que en esta declaración no se especifica el atributo root como lo hicimos en la sección 3.2 Creación del esquema de Unión, esto es debido a que el compilador de esquema asume que el valor de este atributo es false. Por lo que solo necesitamos especificar el valor de este atributo como true cuando queramos que dicho elemento pueda ser usado como elemento raíz. Los elementos con contenido EMPTY también estarán unidos a clases, y su declaración de unión por defecto será: <element name=”nombre_elemento_vacio” type=”class”/> A menos que lo especifiquemos de otra manera en nuestro esquema de unión, el compilador de esquema generará una clase para cada elemento cuyo contenido contenga otros elementos. De la declaración de unión de los elementos que tienen elementos como contenido y/o atributos, el compilador de esquema generará la siguiente definición de clase y constructor: public class Nombre_elemento extends MarshallableObject { public void Nombre_elemento(); 3.2.2 Declaración de Unión de Atributos En los elementos que tienen atributos, dichos atributos toman valores atómicos en vez de valores compuestos, por lo que el compilador de esquema asume que están unidos a propiedades String, como se especifica en la siguiente declaración por defecto: <element name=”nombre_elemento” type=”class”> <attribute name=”nombre_atributo”/> 34 Dentro de la definición de clase del elemento, el compilador de esquema generará la siguiente propiedad para representar al atributo: void setNombre_atributo(String x); String getNombre_atributo(); Podemos observar en esta propiedad, que se acepta y devuelve un String, más adelante en la sección 3.2.5 Especificando Tipos veremos como modificar el esquema para requisitos de uniones particulares de manera que el compilador de esquema genere una propiedad que acepte y devuelva otros tipos de datos. Así como también veremos en la sección 3.2.6 Creación de Tipos de Datos Enumerados, como generar tipos de datos enumerados para la propiedad que representa al atributo. 3.2.3 Declaración de Unión de Contenido La declaración de unión de contenido es lo más complicado, debido a que hay infinitas maneras de especificar el contenido XML, sin embargo JAXB nos lo facilita. El tipo más común de modelo de contenido es una secuencia simple no repetitiva por ejemplo: (a, b, c, d). si utilizamos este modelo de contenido, es muy probable que no tengamos que especificar una declaración de unión para él, ya que el compilador de esquema generará una propiedad separada para cada elemento de la secuencia. Por lo que para los elementos con contenido secuencial simple y no repetitivo (ya sea la raíz u otro elemento), el compilador de esquema asumirá las siguientes declaraciones de unión mostradas en negritas: <element name=”nombre_elemento_raíz” type=”class” root=true> <content> <element-ref name=”nombre_elemento1”/> <element-ref name=”nombre_elemento2”/> </content> </element> <element name=”nombre_elemento” type=”class”> <attribute name=”nombre_atributo”/> <content> <element-ref name=”nombre_elemento1”/> <element-ref name=”nombre_elemento2”/> </content> </element> La declaración de unión element-ref se utiliza para unir un ejemplar de un elemento del modelo de contenido, a una propiedad en la clase del elemento 35 padre. Por defecto, el compilador de esquema genera propiedades String desde todas las declaraciones de uniones element-ref que se refieran a elementos simples. Por lo que la propiedad generada para estos elementos será: public class Nombre_elemento { ... String getNombre_elemento1(); void setNombre_elemento1(String x); Si un elemento contiene algo distinto a una secuencia simple no repetitiva, el compilador de esquema asumirá la declaración de unión de la propiedad general-content, por lo que su declaración será: <element name=”nombre_elemento_raíz” type=”class” root=true> <content property=”content”/> </element> ... <element name=”nombre_elemento” type=”class”> <attribute name=”nombre_atributo”/> <content property=”content”/> </element> Usamos la declaración general-content para unir un modelo de grupo completo (incluyendo modelos de grupos anidados), a una propiedad. Sin embargo, esta declaración no es útil cuando deseamos acceder a los elementos individuales del modelo de contenido. No obstante, puede ser útil para definir uniones mas flexibles si anticipamos que nuestro schema o DTD cambiará en el futuro. Desde estas declaraciones de unión el compilador de esquema genera la siguiente propiedad: List getContent(); void emptyContent(); void deleteContent(); El método getContent devuelve una lista modificable que contiene el valor actual de las propiedades. El método emptyContent descarta los valores de la lista y crea una nueva lista vacía. El método deleteContent borra la lista. Ahora que ya entendemos las declaraciones de unión que el compilador del esquema asumirá basándose en el schema o DTD y el esquema de unión 36 mínimo, podemos escribir fácilmente el esquema de unión. Lo único que necesitamos hacer es escribir las declaraciones de uniones que no son asumidas por el compilador de esquema. 3.2.4 Ejemplo: Esquema de Unión para book.dtd. Como un ejemplo de unir un esquema a las clases, consideremos este DTD: <!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 constructor 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+)> La lista que devuelve getChapter es una lista de valores String, cada uno de los cuales representa un ejemplar distinto del elemento chapter. Observa 37 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. 3.2.5 Especificando Tipos Por defecto, el compilador de esquema genera métodos get que devuelven un String y métodos set que aceptan un String, para todos los elementos y atributos simples. Por ejemplo consideremos las siguientes declaraciones de unión por defecto para el elemento amount: <element-ref name=”amount”/> ... <element name=”amount” type=”value”/> Desde estas declaraciones, el compilador de esquema generará estos dos métodos: public String getAmount(); public void setAmount(String amount); En caso de que deseáramos hacer cálculos con la variable amount tendríamos que convertirla de un String a algún otro tipo de dato que nos lo permita. Para cálculos que implican valores de moneda podemos utilizar el tipo BigDecimal, ya que este tipo representa números decimales con signo y precisión arbitraria. Para especificar un tipo, lo único que tenemos que hacer es utilizar la declaración de conversión para definir la conversión y el atributo convert de la declaración del elemento o del atributo, dependiendo de si queremos convertir el tipo de una propiedad de elemento o de atributo, para referenciar la declaración de conversión. 3.2.5.1 Especificar Tipos No Primitivos Para definir una conversión de String a BigDecimal debemos seguir los siguientes pasos: 38 1. Añadimos en el nivel superior de nuestro esquema de unión, entre las etiquetas <xml-java-binding-schema version=”1.0ea”> y después de la declaración de unión del elemento raíz, la siguiente declaración de conversión: <conversion name=BigDecimal type=java.math.BigDecimal/> Cualquier declaración de unión de elemento o de atributo que quiera utilizar esta conversión se referirá a ella por el nombre BigDecimal, según lo especificado por el nombre del atributo. El valor del tipo de atributo es el tipo real al cual se convierte una propiedad. 2. Para decirle al compilador de esquema que genere una propiedad de un elemento con un tipo BigDecimal tenemos que: Añadir una declaración de unión de elemento para el elemento que queramos convertir en la parte superior de nuestro esquema de unión, como ejemplo utilizaremos al elemento amount de la sección anterior: <element name=amount type=value /> Añadimos un atributo convert a la unión del elemento amount y le asignamos el valor BigDecimal: <element name=amount type=value convert=BigDecimal/> Estas declaraciones de unión producirán las siguientes firmas de métodos: <public java.math.BigDecimal getAmount(); public void setAmount(java.math.BigDecimal amount); Declarar por separado la conversión de una unión de elemento nos permite reutilizar una conversión para otras uniones de elemento. También podemos convertir el tipo de la propiedad de un elemento llamado date a un java.util.Date. Como una fecha puede escribirse de diferentes maneras, debemos especificar como se debe de analizar la fecha cuando se desempaquete y como se debe imprimir cuando se empaquete. Para hacer esta declaración de conversión se requieren los atributos parse y print. 39 Para convertir el elemento date a un java.util.Date, seguimos los siguientes pasos: 1. Añadimos la siguiente declaración de conversión al esquema de unión: <conversión name=TransDate type=java.util.Date parse= TransDate.parseDate print=TransDate.printDate/> El nombre TransDate se refiere a una clase Java que necesitamos proporcionar. Esta clase contiene un método estático parseDate que especifica cómo analizar la fecha y un método estático printDate que especifica cómo imprimir la fecha. 2. Para decirle al compilador de esquema que genere una propiedad date con la clase TransDate, añadimos un atributo convert a la declaración de unión del elemento date y le asignamos el valor de TransDate: <element name=date type=value convert=TransDate/> Estas declaraciones de unión producirán las siguientes firmas de métodos: public java.util.Date getDate(); public void setDate(java.util.Date x); En la conversión a BigDecimal, no necesitamos especificar un método de análisis o de impresión ya que la clase java.math.BigDecimal especifica un constructor que acepta un String y devuelve un BigDecimal y un método toString que acepta un BigDecimal y devuelve un String. Para aplicar la conversión, el compilador de esquema genera código para invocar al constructor y al método toString. 3.2.5.2 Especificar Tipos Primitivos Para especificar tipos primitivos, como int, no necesitamos proporcionar una declaración de unión de conversión separada, simplemente añadimos el atributo convert a la declaración de unión del elemento o del atributo y asignamos el valor al tipo primitivo. Por ejemplo, dentro de la declaración de unión de un elemento llamado check, añadimos una declaración de unión de atributo para el atributo number y especificamos un tipo int para su propiedad: 40 <element name=check type=class > <attribute name=number convert=int /> </element> 3.2.6 Creación de Tipos de Datos Enumerados También podemos utilizar al atributo convert para especificar un tipo de dato enumerado. En el lenguaje de programación Java, representamos tipos enumerados con un tipo seguro enum, que es una clase cuyos elementos representan un conjunto fijo de valores. Un atributo cuyo valor sólo se pueda fijar a uno de un conjunto fijo de valores es un buen candidato para un tipo enumerado. Para generar los tipos para estos atributos tenemos que: 1. Introducir una etiqueta enumeration para cada conversión que queramos realizar, en el nivel superior del esquema de unión, después de la declaración de conversión de TransDate, esto quedaría así: <conversion name=TransDate ... <enumeration/> 2. Dentro de la etiqueta enumeration escribimos el atributo name este nombre debe ser único, debido a que podrían existir mas enumeraciones que estén al mismo nivel en el esquema de unión. <enumeration name=”Categorias1”/> <enumeration name=”Categorias2”/> Estos nombres serán los nombres de las clases para representar los tipos seguros enums. 3. Añadimos el atributo members a cada declaración de enumeración, y le asignamos los posibles valores de cada atributo: <enumeration name=”Categorias1” members=val1 val2 val3/> <enumeration name=”Categorias2” members=val1 val2 val3/> 41 4. Añadimos las declaraciones de unión de los atributos dentro de las declaraciones de unión de los elementos y asignamos el nombre de la declaración de enumeración apropiada a cada atributo convert en la declaración de unión del atributo: <element name=”elemento1” type=”class”> <attribute name=”nombre_atributo” convert=Categorias1/> ... <element name=”elemento2” type=”class”> <attribute name=”nombre_atributo” convert=Categorias2/> 5. Desde la declaración de unión del atributo del elemento2, el compilador de esquema generará la siguiente clase: public final class Categorias2 { public final static Categorias2 VAL1; public final static Categorias2 VAL2; public final static Categorias2 VAL3; public static Categorias2 parse(String x); public String toString(); } 3.2.7 Creación de interfaces Cuando tenemos un grupo de clases que proporcionan funciones similares y tienen algún comportamiento y propiedades comunes, podemos utilizar un interface para capturar las semejanzas entre las clases. Para que el compilador de esquema genere un inteface lo que tenemos que hacer es: 1. En cualquier lugar del nivel más alto de nuestro esquema de unión, quizás después de las declaraciones de enumeración, introducimos la siguiente declaración de interface: <interface name=Entry members=clase1 clase2 clase3 properties=elemento_comun_amount elemento_comun_date /> El atributo members representa todas las clases que implementan la interface. El atributo properties representa el contenido común compartido por los miembros de la interface. 42 2. Ahora, suponiendo que los elementos clase1, clase2, clase3 ocurren en el modelo de contenido de la raíz como un grupo de elección, necesitamos referenciar Entry desde la unión del grupo de elección. Previamente asignamos el nombre de la interface al atributo de la propiedad, el valor list al atributo collection y asignamos el nombre de la interface al atributo supertype: <element name= elemento_raiz type=class class= Elemento_raiz> <content> <choice property=entries collection=list supertype=Entry/> </content> </element> El atributo supertype indica una clase o interface declarada en el esquema de unión, que cada clase de elemento incluida en la propiedad choice implementa. 3. Estas declaraciones de unión producirán una interface llamada Entry, que incluirá las propiedades elemento_comun_amount y elemento_comun_date: public interface Entry { ... public int getElemento_comun_amount(); public void setElemento_comun_amount(int x); public Date getElemento_comun_date(); public void setElemento_comun_date(Date d); 3.3 Generación de los Ficheros Fuente de Java Ya que tenemos el esquema de unión, podemos ejecutar al compilador de esquema para generar un conjunto de ficheros fuente Java desde el schema o DTD basándose en las instrucciones que proporcionamos en el esquema de unión. Después de que se generen los ficheros fuente, podemos compilarlos usando el compilador de Java como lo haríamos con cualquier aplicación Java. Para esto seguimos los siguientes pasos: 1. Ejecutamos el compilador de esquema con el archivo del schema o DTD y el esquema de unión que hemos creado: xjc fichero.dtd fichero.xjs 43 Ahora deberíamos ver los ficheros con extensión .java (fichero.java y Entry.java) en nuestro directorio actual. 2. Compilamos los ficheros fuente en clases java con: javac *.java Un elemento raíz del schema o DTD, está unido a la clase con el mismo nombre del elemento y su firma es: public class nombre_elemento extends MarshallableRootElement implements RootElement Como este elemento es un elemento raíz, la clase extiende MarshallableRootElement, que es la clase que representa objetos elemento raíz que pueden ser empaquetados y desempaquetados, e implementa RootElement. Aunque MarshallableRootElement define métodos marshal, que la clase del elemento raíz usa por extensión, no define ningún método unmarshal. Por lo que, el compilador de esquema genera estos métodos unmarshal estáticos dentro de la clase: public static Nombre_elemento unmarshal(InputStream in) public static Nombre_elemento unmarshal(XMLScanner xs) public static Nombre_elemento unmarshal(XMLScanner xs, Dispatcher d) Cuando desempaquetamos un documento XML, podemos invocar a unmarshal (InputStream) o a unmarshal (XMLScanner) en los que InputStream o XMLScanner representan nuestro documento XML. Aunque el desempaquetamiento realiza la validación por nosotros, necesitamos validar después de editar el árbol de contenido y antes de empaquetarlo en un documento XML. Para este propósito, el compilador de esquema genera estos métodos: public void validateThis(); public void validate(); Después de editar una parte del árbol de contenido, podemos usar validateThis para validar el objeto editado. Antes de empaquetar el árbol de contenido a un documento XML, debemos usar validate para validar todo el árbol de contenido. 44 3.4 Configuración de la aplicación JAXB Ahora antes de poder usar JAXB para construir representaciones de datos o trabajar con los datos, lo primero que necesitamos es crear una aplicación Java que realice estas funciones. Para configurar nuestra aplicación JAXB hacemos lo siguiente: 1. Creamos un fichero con extensión .java, ficheroapp.java. 2. Importamos los siguientes paquetes: import java.io.*; import java.util.*; import javax.xml.bind.*; import javax.xml.marshal.*; Estos dos últimos paquetes son parte del marco de trabajo de unión, que define los métodos unmarshal, marshal, y validate. 3. Declaramos la clase ficheroapp de la siguiente manera: public class ficheroapp { } 4. Inicializamos los objetos del elemento raíz: public static elemento_raíz obj1 = new elemento_raíz (); public static elemento_raíz obj2 = new elemento_raíz (); 5. Finalmente dentro de la clase ficheroapp creamos nuestro método main: public class ficheroapp { public static void main(String args[]) throws Exception { } } 3.5 Construcción de 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 45 trabajo es el API de tiempo de ejecución que usan las clases generadas para soportar tres operaciones primarias: Desempaquetar: el proceso de producir un árbol de contenidos desde un documento XML. Validación: 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. Sin embargo, a lo que nos centraremos en este tema es a Crear Objetos de Java a partir de Esquemas XML. 3.5.1 Desempaquetar (Construcción del árbol de Contenido) 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 46 XML. De forma semejante, 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. Consideremos un fichero llamado march.xml, que contiene transacciones escritas en el mes de marzo. Para desempaquetar este documento XML en un árbol de contenido en el fichero CheckbookApp.java, tenemos que hacer lo siguiente: 1. Creamos un metodo llamado buildTrees: public static void buildTrees() throws Exception { } 2. En nuestro nuevo método leemos el fichero XML en un FileInputStream: File march = new File("march.xml"); FileInputStream fIn = new FileInputStream(march); 3. 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(); } . 4. Llamamos al método buildTrees desde nuestro método main: buildTrees(); En este punto, CheckbookApp genera un árbol de contenido desde march.xml. 3.5.1.1 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 47 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 tenemos que: 1. 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); 2. Seleccionamos el nombre de la entidad que recibe el cheque: aprilRentCheck.setName(Gilchrest Gardens Manor); 3. 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. 4. 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 la seccion 3.2.5.1 Métodos no Primitivos porque especificamos nuestro propio formato de fechas, que es:MM-dd-yyyy. 5. 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. 6. Seleccionamos el estado del cheque a pendiente: Pending pending = new Pending(); aprilRentCheck.setPendVoidClrd(pending); 48 7. 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 que representan funciones comunes, implementan Entry. 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. 3.5.2 Validación El proceso de desempaquetar 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 cualquier momento después de haber construido el árbol de contenido usando los 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. 3.5.3 Trabajar con Datos (Acceso al árbol de Contenido) 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, utilizando las propiedades de las clases generadas. Para proporcionar funcionalidades específicas de la aplicación, podemos extender las clases en vez de sólo utilizarlas directamente. Por ejemplo, además de acceder a un trozo de datos, también podríamos querer realizar algún cálculo con los datos, para hacerlo podemos proporcionar estas funcionalidades en una subclase de una clase derivada. 49 A continuación mostraremos cómo utilizar las clases para construir representaciones de datos desde documentos XML y trabajar con los datos. 3.6 Creación de Objetos de Java a partir de Esquemas XML Esta sección describe como JAXB representa el contenido de XML como objetos en Java. Específicamente, trataremos la Unión de esquemas XML a Identificadores Java. Después de la lectura de esta sección, deberíamos sentirnos bastante cómodos con JAXB ya que podremos generar clases Java JAXB de un esquema XML. 3.6.1 La Representación Java de Esquema XML JAXB apoya la agrupación de clases generadas e interfaces en paquetes Java. Un paquete comprende: Un nombre, que es sacado directamente del XML en el espacio de nombre URI, o especificado por una personalización obligatoria del XML . Un juego de interfaces Java que representan el contenido de los modelos dentro del esquema. Un Juego de interfaces de elementos Java que representan declaraciones de elemento que ocurren dentro del esquema. La claseAnObjectFactory contiene: Una instancia de métodos de caso para cada interfaz Java contenido en el interfaz e interfaces de elementos Java dentro del paquete; por ejemplo, considerando un contenido de interfaz Java llamado Foo, el método derivado sería: public Foo createFoo() throws JAXBException; Atributario de fábrica de caso Dinámico; crea una instancia del interfaz especificado en el contenido del interfaz de java; por ejemplo: public Object newInstance(Class javaContentInterface) throws JAXBException; 50 getProperty y setProperty APIs que permite la manipulación de propiedades proveer-especificaciones. Un Juego de typesafe enumerado de clases contiene paquetes javadoc. 3.6.2 Archivos Básicos Cada directorio de ejemplo contiene varios archivos básicos: po.xsd es el esquema XML que usaremos como entrada al JAXB el compilador obligatorio, y del cual derivara el esquema JAXB del que las clases Java serán generadas. po.xml es el archivo que contiene el simple contenido XML. Main.java es la clase principal Java para cada ejemplo. build.xml es un archivo de proyecto Ant proveído para su conveniencia. Ant se emplea para generar, compilar, y correr el esquema de clases JAXB automáticamente. El archivo build.xml varía a través de los ejemplos. MyDatatypeConverter.java puede ser una clase Java usada para proporcionar el acostumbrado tipo de dato para conversión. binding.xjb son un archivo de declaraciones externo obligatorio que es pasado al compilador de uniones JAXB para personalizar la unión por default JAXB. example.xsd es un archivo de esquema corto que contiene conflictos de nombramiento deliberados. 3.6.3 Escribir un esquema de unión Como vimos anteriormente, el esquema de unión contiene las instrucciones de cómo unir un DTD a las clases. La Descripción de cómo realizar el esquema de unión fue vista en la sección 3.2 Creación del esquema de unión. 3.6.4 Ejecutar el compilador de esquema Enseguida debemos ejecutar el compilador de esquema con el DTD y el esquema de unión como entradas, para generar el código fuente. 51 3.6.4.1 La configuración y la ejecución El archivo build.xml incluido en cada directorio de ejemplo es un archivo del proyecto que, cuando corre, automáticamente realiza los siguientes pasos: 1. Actualiza su CLASSPATH para incluir lo necesario por esquema JAXB que deriva las clases. 2. Corre la unión del compilador JAXB para generar clases java JAXB del esquema fuente XML, po.xsd, y pone las clases en un paquete llamado primer.po. 3. Genera la documentación API del sacado por esquema JAXB derivando las clases que usan el instrumento Javadoc. 4. Compila las clases derivadas por el esquema JAXB. 5. Corre el Main para el ejemplo. 3.6.4.2 Opciones de la compilación de JAXB El esquema JAXB del compilador de unión es localizado en el directorio <JWSDP_HOME>/jaxb/bin. Hay dos escrituras en este directorio: xjc.sh (Solaris/Linux) y xjc.bat (Windows). Tanto xjc.sh como xjc.bat toman las mismas opciones de línea de mando. Podemos mostrar instrucciones de uso rápidas invocando las escrituras sin cualquier opción, o con el interruptor de ayuda. La sintaxis es como sigue:. xjc [-options ...] <schema> // <schema> uno o mas archivos de esquemas para compilar 52 3.6.4.3 Acerca de los esquemas de unión para Java en la compilación Cuando usted corre el compilador de unión JAXB contra el po.xsd el esquema XML usado en los ejemplos básicos, el compilador de unión JAXB genera un paquete Java llamado primer.po conteniendo once clases, fabricación de un total de doce clases en cada uno de los ejemplos básicos: Estas clases y sus uniones específicas a la fuente XML el esquema para los ejemplos básicos son descritas enseguida: 53 3.6.5 Ejemplo: Generar Clases desde un DTD Como ejemplo de generación de clases desde un DTD, consideremos el siguiente DTD, que se llama "priceList.dtd". <!ELEMENT priceList (coffee)+ > <!ELEMENT coffee (name, price)> <!ELEMENT name (#PCDATA)> <!ELEMENT price (#PCDATA)> El compilador de esquema JAXB es suficientemente poderoso como para hacer asunciones razonables desde el DTD y el esquema de unión que especifica sólo el elemento raíz del documento. Todo lo que necesitamos especificar en el esquema de unión es que el elemento price es convertido a una propiedad que devuelve y acepta un BigDecimal: ... <element name="priceList" type="class" class="PriceList" root="true"/> <element name="price" type="value" convert="BigDecimal"/> <conversion name="BigDecimal" type="java.math.BigDecimal"/> 54 ... Desde este DTD y este esquema de unión, el compilador de esquema genera una clase PriceList y una clase Coffee. La clase PriceList incluye un constructor y una lista de propiedades, a las que está unida el elemento coffee. La clase Coffee contiene un constructor y una propiedad para representar el nombre del café y una propiedad para representar el precio. Los métodos para acceder al precio son: BigDecimal getPrice(); void setPrice(BigDecimal x); Las dos clases también contienen métodos para desempaquetar, validar y empaquetar. Recordemos que, Desempaquetar es el proceso de construir una representación objeto del dato XML. La validación es el proceso de chequear si el objeto está conforme a las especificaciones del DTD. Y Empaquetar es el proceso de generar datos XML desde una representación objeto. 3.6.5.1 Construir Representaciones Objeto de Datos XML Después de generar nuestras clases, podemos escribir aplicaciones Java que las usen para construir representaciones objeto del documento XML que sean válidos con respecto al DTD. Cada objeto corresponde a un elemento del documento XML. De forma similar, cada objeto es un ejemplar de una clase del conjunto de clases generadas. Como los objetos se mapean tanto al documento como a las clases, tenemos dos formas diferentes de construir el árbol de objetos Java: desempaquetando un documento XML válido, o ejemplarizando objetos desde las clases. De esta forma, JAXB nos permite procesar documentos XML existentes y crear nuevos datos XML ejemplarizando las clases generadas. Supongamos que tenemos este documento XML: <priceList> <coffee> <name>Arabica</name> <price>13.50</price> </coffee> <coffee> <name>Mocha Java</name> <price>11.95</price> 55 </coffee> <coffee> <name>Sumatra</name> <price>12.50</price> </coffee> </priceList> Para desempaquetar este documento XML, creamos un stream de entrada desde él y llamamos al método unmarshal de la clase PriceList: FileInputStream fis = new FileInputStream("priceList.xml"); PriceList myPrices = PriceList.unmarshal(fis); Ahora tenemos un árbol de objetos Java con el objeto myPrices como la raíz del árbol. Supongamos que queremos crear nuestra propia lista de precios de cafés como un documento XML. Primero creamos el árbol de objetos por ejemplarización y luego empaquetamos el árbol en un documento XML. Para crear un árbol de objetos usando ejemplarización, creamos un objeto PriceList, obtenemos la lista de objetos Coffee desde él, creamos un nuevo objeto Coffee, y lo añadimos a la lista: PriceList myNewPrices = new PriceList(); List listOfCoffees = myNewPrices.getCoffees(); Coffee zCoffee = new Coffee(); zCoffee.setName("Zapoteca"); zCoffee.setPrice("15.00"); listOfCoffees.add(zCoffee); Una vez que tenemos el dato XML en forma de un árbol de objetos, podemos trabajar con los datos como con cualquier otro objeto Java. De esta forma, JAXB proporciona un interface de programación Java para XML y nos permite la integración de datos XML dentro de aplicaciones Java. 3.7 Creación de Esquemas XML a partir de Objetos de Java. Como vimos en la sección 3.5 Construccion de Representaciones de Datos, al proceso de producir un documento XML desde objetos java, se le llama Empaquetar. 56 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. El 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 siguiente ilustra dos formas para construir representaciones de datos: 3.7.1 Validación del árbol de Contenido 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. Utilicemos el ejemplo de la sección 3.5.1 Desempaquetar (Construcción del árbol de Contenido) Para validad ámbos árboles de contenido tenemos que: 57 1. Creamos un método llamado validateTrees: public static void validateTrees() throws Exception {} 2. Dentro del método, llamamos a validate sobre marchTrans y aprilTrans: marchTrans.validate(); aprilTrans.validate(); 3. Llamamos a validateTrees desde el método main: validateTrees(); 3.7.2 Empaquetar el árbol de Contenido a Documentos XML 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: 1. Creamos un nuevo método llamado marshalTrees: public static void marshalTrees() throws Exception {} 2. 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); 3. Creamos el objeto OutputStream para enviarlo al método marshal: FileOutputStream fMOut = new FileOutputStream(march_new); FileOutputStream fAOut = new FileOutputStream(april_new); 4. Llamamos al método marshal sobre cada árbol: try { marchTrans.marshal(fMOut); aprilTrans.marshal(fAOut); } finally { fMOut.close(); fAOut.close(); 58 } 5. 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. 59 Parte IV. CREANDO UNA APLICACIÓN JAXB 4.1 Consideraciones Previas Primero que nada, debemos mencionar que software estamos utilizando y los requerimientos para el desarrollo de los siguientes ejemplos. Netbeans 5.5.1 (entorno IDE) JAXB 2.0 Java Application Platform SDK Máquina Virtual de Java (Jdk 1.6) Advertencia: los ejemplos siguientes se recomienda ejecutarlos sobre estos paquetes, si se implementan sobre otras versiones pueden no funcionar. Ya que en nuestra recopilación de información tuvimos desagradables y frustrantes experiencias por no mencionar lo anterior. Además se recomienda no utilizar espacios en los nombres de los proyectos, archivos o rutas a utilizar para evitar problemas. 4.2 EJEMPLO 1: Utilizando la operación unmarshal Para el desarrollo de una aplicación java utilizando JAXB se requiere hacer los siguientes pasos generales: Crear un proyecto Aplicación Java en NetBeans. Definir el esquema fuente. Crear un documento XML que se valide con el esquema fuente. Compilar el esquema con la aplicación del compilador JAXB (xjc.bat en nuestro caso), el cual generará una carpeta conteniendo los archivos .java por default en la ruta donde se encuentre el archivo xjc.bat. Añadir el paquete de los archivos generados por el compilador JAXB. Añadir los archivos .jar . Redefinir el método main para manipular las clases generadas anteriormente A continuación se describe detalladamente el proceso de creación, compilación e implementación JAXB. 60 1. Creamos una aplicación java en NetBeans (menú File, New Proyect, carpeta general, Java Aplication) y le ponemos el nombre prueba. No se nos olvide cambiar la ruta de ubicación del proyecto a la unidad C, si no eres un experto y quieres evitarte posibles complicaciones. 2. Ahora creamos un esquema el cual definirá dos tipos de listas principales: una de negocios y otra de personas. Si no comprendes el esquema consulta la sección 2.3 Xml Schema. Menu file, new_file, carpeta xml, xml eschema. <?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://xml.netbeans.org/schema/schema" xmlns:tns="http://xml.netbeans.org/schema/schema" elementFormDefault="qualified"> <xsd:element name="Root" type="tns:TipoRoot"/> <xsd:complexType name="TipoRoot"> <xsd:sequence> <xsd:element name="ListaPersona" type="tns:TipoListaPersona"/> <xsd:element name="ListaNegocio" type="tns:TipoListaNegocio"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="TipoListaPersona"> <xsd:sequence> <xsd:element name="Persona" type="tns:TipoPersona" minOccurs="1" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="TipoPersona"> <xsd:sequence> <xsd:element name="nombre" type="xsd:string"/> <xsd:element name="apellido" type="xsd:string"/> <xsd:element name="fechaNacimiento" type="xsd:date"/> <xsd:element name="id" type="xsd:int"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="TipoListaNegocio"> <xsd:sequence> <xsd:element name="Negocio" type="tns:TipoNegocio" minOccurs="1" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="TipoNegocio"> <xsd:sequence> 61 <xsd:element name="nombre" type="xsd:string"/> <xsd:element name="codigo" type="xsd:int"/> </xsd:sequence> </xsd:complexType> </xsd:schema> 3. Creamos un documento xml (file, new file, carpeta xml, documento xml), next (escribimos el nombre platillos), next (elegir la opcion xml-schemaconstrained-document), next (en schema url elegimos la direccion donde se encuentra el eschema creado en el paso anterior en la opcion root element) y por utimo escribimos el siguiente documento. <?xml version="1.0" encoding="UTF-8"?> <!-Document : f.xml Created on : 7 de noviembre de 2007, 03:47 PM Author : francisco Description: Purpose of the document follows. --> <Root xmlns='http://xml.netbeans.org/schema/schema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://xml.netbeans.org/schema/schema file:/C:/Documents%20and%20Settings/francisco/prueba/src/f.xsd'> <ListaPersona> <Persona> <nombre>Antonia</nombre> <apellido>Lopez</apellido> <fechaNacimiento>1887-10-12</fechaNacimiento> <id>1</id> </Persona> <Persona> <nombre>Poncho</nombre> <apellido>Rojas</apellido> <fechaNacimiento>1986-12-12</fechaNacimiento> <id>1</id> </Persona> </ListaPersona> <ListaNegocio> <Negocio> <nombre>Microsoft</nombre> 62 <codigo>0</codigo> </Negocio> <Negocio> <nombre></nombre> <codigo>IBM</codigo> </Negocio> </ListaNegocio> </Root> 4. Ahora vamos a compilar el esquema anterior con el compilador de JAXB para generar las clases .JAVA para empezar iniciamos la línea de comandos cmd.exe, (inicio, ejecutar, y tecleamos cmd), una vez ahí, cambiarse al directorio donde se encuentra instalada java plataforma Application que en nuestro caso se encuentra en la ruta c:\Sun\SDK\bin\ . una vez estando en esta carpeta ejecutamos la siguiente sentencia (dependiendo de la ruta donde esté nuestro esquema). Para evitarnos complicaciones, hemos copiamos el esquema fuente al directorio raíz, por lo que la sentencia quedaría así: xjc c:\f.xsd Lo ejecutamos: Algo que debemos mencionar es que el compilador enlazante permite o acepta ciertas opciones adicionales al momento de generar las clases para nuestro esquema; en específico, podemos observar la existencia de un parámetro “de más”, -p ejemplo, donde -p nos ayuda a redefinir el paquete donde se van a generar nuestras clases. El formato general de la instrucción xjc es: xjc [-options ...] <schema> Donde las opciones más comunes pueden ser: 63 Argumento opción <schema> -nv Descripción Uno o más archivos de esquemas a compilar No realiza una validación estricta en el esquema de entrada. Por default xjc realiza una validación esetricta de el esquema fuente antes de procesarlo.Simplemente hace un poco menos de validación -extension Por default, xjc estrictamente fuerza las reglas obtenidas de la compatibilidad de la especificación de JAXB; usando el cambio de extension puedes activar Extensiones de vendedores. -b <file> Especifica uno o más archivos vinculantes externos para procesar (cada archivo debe tener su propio –b switch). Puedes tener un único archivo vinculante que contiene las personalizaciones de múltiples esquemas, o puedes romper las personalizaciones en múltiples archivos vinculantes. Por default, xjc genera las clases de contenido en el directorio actual. Usa esta opción para especificar un directorio alternativo. El directorio debe de existir; xjc no creará el directorio por ti. Especifica donde encontrar los archivos de una clase aplicación del cliente usadas por las personalizaciones <jxb:javaType> y <xjc:superClass> . -d <dir> -classpath <arg> -xml schema -dtd -help Trata el esquema de entrada como un Schema W3C XML(por default) Trata el esquema de entrada como un XML DTD Muestra este mensaje de ayuda 5. Como se muestra en la pantalla el compilador de JAXB ( xjc.bat ) genera automáticamente los archivos .java los cuales se crearon en la carpeta donde se encuentra el archivo de xjc, dentro de una carpeta de nombre ejemplo. Continuando, vamos a anexar al proyecto los archivos generados .java; para realizar esto existen varias formas, una de las más sencillas es simplemente copiar la carpeta generada por la ejecución del comando xjc, y pegarla en la ruta donde se creó nuestro proyecto en NetBeans, en la carpeta scr. 64 6. Una vez agregados los .java vamos agregar los .jar, click derecho en la carpeta librería elejimos la opcion ADD JAR\Folder nos vamos a la ruta donde tenemos instalado el JDK 6.0 y agregamos los archivos .jar mostrados a continuación : 7. Ahora viene lo interesante, utilizar las clases java generadas y realizar las operaciones de unmarshall para este ejemplo (la página fuente se encuentra en :). package prueba; import java.io.FileInputStream; import java.util.Iterator; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; 65 import ejemplo.TipoListaNegocio; import ejemplo.TipoListaPersona; import ejemplo.TipoNegocio; import ejemplo.TipoPersona; import ejemplo.TipoRoot; public class Main { /** Creates a new instance of Main */ public Main(){ try { //"org.nextbeans.xml.schema.schema" -> Nombre del paquete donde encontrar las clases generadas con el //compilador de JAXB (xjc) Unmarshaller unmarshaller = JAXBContext.newInstance("ejemplo").createUnmarshaller(); //Es necesario el cast a JAXBElement y luego la aplicacion del metodo getValue() porque el unmarshal no retorna el objeto de la clase TipoRoot. //"xml/myFile.xml" archivo xml donde se encuentran los datos TipoRoot root = (TipoRoot)((JAXBElement)unmarshaller.unmarshal( new FileInputStream("src/f.xml"))).getValue(); TipoListaPersona tipoListaP = root.getListaPersona(); Iterator<TipoPersona> it = tipoListaP.getPersona().iterator(); System.out.println("*********Lista Persona*********"); while ( it.hasNext() ){ TipoPersona tp = it.next(); System.out.println("El nombre es == " + tp.getNombre()); System.out.println("El apellido es == " +tp.getApellido()); System.out.println("La fecha de nacimiento es == tp.getFechaNacimiento().toString() ); System.out.println("El id es == " + tp.getId()); } System.out.println("********Lista Negocio**********"); TipoListaNegocio listaNegocio = root.getListaNegocio(); Iterator<TipoNegocio> itNg = listaNegocio.getNegocio().iterator(); while ( itNg.hasNext() ){ TipoNegocio tn = itNg.next(); System.out.println("El nombre es == " + tn.getNombre()); System.out.println("El codigo es == " + tn.getCodigo()); } " + } catch (Exception ex) { ex.printStackTrace(); } 66 } /** * @param args the command line arguments */ public static void main(String[] args) { Main m = new Main(); } } 8. Ejecutamos nuestra aplicación. Si todo se hizo correctamente debería de aparecerte algo como lo que se muestra a continuación en la ventana inferior del IDE NetBeans (ventana de salida). init: deps-jar: Compiling 1 source file to C:\Documents and Settings\francisco\prueba\build\classes compile: *********Lista Persona********* El nombre es == Antonia El apellido es == Lopez La fecha de nacimiento es == 1887-10-12 El id es == 1 El nombre es == Poncho El apellido es == Rojas La fecha de nacimiento es == 1986-12-12 El id es == 1 ********Lista Negocio********** El nombre es == Microsoft El codigo es == 0 El nombre es == IBM El codigo es == 1 debug: BUILD SUCCESSFUL (total time: 2 seconds) 4.3 EJEMPLO 2: Utilizando la operación marshal Una vez que tenemos el resultado de los pasos mencionados previamente para el ejemplo anterior, procedemos a utilizar el objeto Marshal para poder realizar la operación de empaquetación u organización de un árbol de contenido Java en una salida XML. 67 Para hacer lo anterior solo modificamos el constructor Main(), y hacemos que asemeje a lo siguiente: //Además de las sentencias import que ya tenemos agregamos las siguientes import ejemplo.ObjectFactory; import javax.xml.bind.Marshaller; //........ lo que ya estaba public Main(){ try { //"org.nextbeans.xml.schema.schema" -> Nombre del paquete donde //encontrar las clases generadas con el //compilador de JAXB (xjc) Unmarshaller unmarshaller = JAXBContext.newInstance("ejemplo").createUnmarshaller(); //Es necesario el cast a JAXBElement y luego la aplicacion del metodo getValue() porque el unmarshal no retorna el objeto de la clase TipoRoot. //"xml/myFile.xml" archivo xml donde se encuentran los datos TipoRoot root = (TipoRoot)((JAXBElement)unmarshaller.unmarshal( new FileInputStream("src/f.xml"))).getValue(); //Comenzamos con la operación de unmarshal JAXBContext jaxbContext = JAXBContext.newInstance("ejemplo"); Marshaller marshaller = jaxbContext.createMarshaller(); ObjectFactory factory = new ObjectFactory(); JAXBElement<TipoRoot> er = factory.createRoot(root); marshaller.setProperty("jaxb.formatted.output",Boolean.TRUE); marshaller.marshal(er, System.out); } catch (Exception ex) { ex.printStackTrace(); } } Si lo hiciste correctamente y si no hay algún otro detalle, al ejecutar el proyecto te debería de aparecer en la ventana de salida lo siguiente: init: deps-jar: Compiling 1 source file to C:\Documents and Settings\francisco\prueba\build\classes compile: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Root xmlns="http://xml.netbeans.org/schema/schema"> 68 <ListaPersona> <Persona> <nombre>Antonia</nombre> <apellido>Lopez</apellido> <fechaNacimiento>1887-10-12</fechaNacimiento> <id>1</id> </Persona> <Persona> <nombre>Poncho</nombre> <apellido>Rojas</apellido> <fechaNacimiento>1986-12-12</fechaNacimiento> <id>1</id> </Persona> </ListaPersona> <ListaNegocio> <Negocio> <nombre>Microsoft</nombre> <codigo>0</codigo> </Negocio> <Negocio> <nombre>IBM</nombre> <codigo>1</codigo> </Negocio> </ListaNegocio> </Root> debug: BUILD SUCCESSFUL (total time: 2 seconds) 4.4 EJEMPLO 3: Definiendo un árbol de contenido y transformarlo en formato XML En este sencillo ejemplo vamos a seguir trabajando con el proyecto creado anteriormente. Vamos a utilizar las clases generadas por el compilador JAXB para poder construir manualmente un árbol de contenido y posteriormente transformar este árbol en formato XML y visualizarlo en la salida estándar(generalmente la pantalla). Abrimos nuestro proyecto prueba, y nos posicionamos en el constructor de la clase Main(). Luego modificamos el código de tal forma que quede similar al siguiente: public Main(){ 69 try { //Creamos un objeto JAXBContext JAXBContext jaxbContext = JAXBContext.newInstance("ejemplo"); //Creamos un objeto de la clase Marshaller y ObjectFactory para poder //crear los elementos individuales del árbol java Marshaller marshaller = jaxbContext.createMarshaller(); ObjectFactory factory = new ObjectFactory(); //Definimos las variables que contendrán los datos de una persona y un negocio TipoNegocio tn = factory.createTipoNegocio(); TipoPersona tp = factory.createTipoPersona(); //Definimos los datos de el negocio tn.setNombre("ALICA"); tn.setCodigo(3); //Definimos los datos de la persona tp.setNombre("CINTHIA"); tp.setId(5); tp.setApellido("MATA"); //Creamos una Lista de personas y una lista de negocios TipoListaNegocio tln = factory.createTipoListaNegocio(); TipoListaPersona tlp = factory.createTipoListaPersona(); //Agregamos a las persona y al negocio antes especificado a sus correspondientes // listas tln.getNegocio().add(tn); tlp.getPersona().add(tp); //Creamos una instancia del elemento TipoRoot TipoRoot r= factory.createTipoRoot(); //Agregamos al árbol de contenido las listas de negocios y personas r.setListaNegocio(tln); r.setListaPersona(tlp); //Creamos un elemento JAXB para poder transformar el árbol de contenido ya //generado JAXBElement<TipoRoot> er = factory.createRoot(r); //Establecemos el formato de salida y realizamos la operación de marshal marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE); marshaller.marshal(er, System.out); } catch (Exception ex) { ex.printStackTrace(); 70 } } El resultado de la ejecución del proyecto: 4.5 EJEMPLO 4: Generando un esquema XML a partir de clases java JAXB 2.0 soporta las transformaciones bidireccionales de los documentos XML y árboles de contenido Java. Además, dentro de la carpeta bin (donde se encuentra el archivo del comando, xjc.bat) existe un archivo el cual nos puede ser de gran utilidad, ya que puede generar, a la inversa del xjc, esquemas XML a partir de clases java. El archivo-comando que hace posible esto es el schemagen.bat, el cual recibe como parámetro de entrada la ruta de la clase raíz java, de la cual generará el esquema. 71 Recordemos que anteriormente generamos el conjunto de clases java a partir de un esquema, ahora vamos a hacer lo inverso con el comando schemagen.bat, pasándole como argumento la ruta del archivo TipoRoot.java: Una vez hecho lo anterior checamos que nos haya creado el esquema en la ruta donde tenemos el archivo schemagen.bat. Obtenemos un archivo denominado shcema1.xsd que contiene lo siguiente: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="TipoRoot"> <xs:sequence> <xs:element name="ListaPersona" type="TipoListaPersona"/> <xs:element name="ListaNegocio" type="TipoListaNegocio"/> </xs:sequence> </xs:complexType> <xs:complexType name="TipoListaPersona"> <xs:sequence> <xs:element name="Persona" type="TipoPersona" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="TipoPersona"> <xs:sequence> <xs:element name="nombre" type="xs:string"/> <xs:element name="apellido" type="xs:string"/> <xs:element name="fechaNacimiento" type="xs:anySimpleType"/> <xs:element name="id" type="xs:int"/> </xs:sequence> </xs:complexType> <xs:complexType name="TipoListaNegocio"> <xs:sequence> <xs:element name="Negocio" type="TipoNegocio" maxOccurs="unbounded"/> </xs:sequence> 72 </xs:complexType> <xs:complexType name="TipoNegocio"> <xs:sequence> <xs:element name="nombre" type="xs:string"/> <xs:element name="codigo" type="xs:int"/> </xs:sequence> </xs:complexType> </xs:schema> 73 ANEXOS Proceso de Instalación de JAXB 2.0 Los ejemplos de esta obra son para trabajar JAXB 2.0 junto con 5.5.1 NetBeans Software necesario para instalar el JAXB Plataforma jdk1.6.0_02 jwsdp-2_0-windows-i586 paquete WSDP java_app_platform_sdk-5_02-windows-nojdk tomcat 5.0 solo funciona con esta versión no se instala se descomprime en c:\. Una ves que se cuente con los anteriores software lo que hay que hacer es seguir los pasos. 1. Instalar primero la plataforma jdk1.6.0_02 una vez instalada instalar el java_app_platform_sdk-5_02-windows-nojdk ambas instalaciones no tienen ninguna complicación Nota: si ya tienes instalado el netbeans ya tienes instalada la plataforma jdk-. 2. Ejecuta el paquete Jwsdp-2_0. 74 3. Aparece el asistente de la instalación presione siguiente (next). 4. En la siguiente ventana acepta la licencia de uso y presiona siguiente para continuar. 5. En la cuarta ventana elige la maquina virtual sobre la cual el WSDP ejecutara la busca por default. En caso de no tenerla no se podrá seguir con la instalación. 6. Antes de continuar con la instalación debemos descomprimir el archivo tomcat 5.0 en la unidad c:\ para evitar complicaciones. 75 7. En la ventana de instalación click en browse seleccionar la ruta de la carpeta de tomcat 5.0 y click siguiente. 8. En tipo de instalación seleccionar instalación typical. 9. Definimos el usuario tomcat. 76 Nota: este usuario debe estar definido en un archivo de tomcat de nombre manager el cual esta dentro de la carpeta server: <tomcat-users> <user name="tomcat" password="tomcat" roles="tomcat" /> 10. En este paso hay que crear la siguiente ruta c:\archivo de programas\java\jdk1.6.0_02\jre\lib\endorsed y en esta ubicación hay que copiar los archivos que se encuentran en esta otra ubicación c:\sun\jwsdp-2.0\jaxp\lib\endorsed. 11. En la instalación click en siguiente y por último finalizar. 77 INDICE PARTE I. INTRODUCCIÓN 1 1.1 ¿Qué es JAXB? 1 1.2 Características de JAXB 1.2.1 Las aplicaciones JAXB usan Tecnología Java y XML 1.2.2 Las Aplicaciones JAXB son Rápidas 1.2.3 Las Aplicaciones JAXB son Fáciles de Crear y de Usar 1.2.4 Las Aplicaciones JAXB Pueden Convertir Datos 1.2.5 Las Aplicaciones JAXB Pueden Personalizarse 1.2.6 Las Aplicaciones JAXB son Extensibles 1 2 3 4 4 5 6 1.3 Arquitectura de JAXB 6 1.4 El proceso de vinculación de JAXB 8 1.5 Framework vinculante JAXB 1.5.1 El paquete javax.xml.bind 1.5.1.1 Desorganización 1.5.1.2 La Organización 1.5.1.3 La Validación 10 10 11 12 12 1.6 Versiones 13 PARTE II. DOCUMENTOS XML 14 2.1 ¿Qué es XML? 14 2.2 Definición de Tipo de Documento (DTD) 2.2.1 Declaración de Tipo de Documento 2.2.2 Declaración de Elementos 2.2.2.1 Elementos vacíos 2.2.2.2 Elementos con sólo elementos 2.2.2.3 Elementos de Contenido Mixto 2.2.2.4 Elementos ANY 2.2.3 Declaración de Atributos 2.2.3.1 Atributos CDATA y NMTOKEN 2.2.3.2 Atributos enumerados y notaciones 2.2.3.3 Atributos ID e IDREF 15 16 17 17 18 22 22 23 24 24 25 2.3 XML Schema 2.3.1 Estructura Mínima de un Esquema 2.3.2 Tipos de Datos 2.3.3 Diagrama de los Tipos de Datos que manejan los Schemas 25 26 27 28 2.4 Documento XML 2.4.1 Ejemplo: Diferencias entre DTD y Schemas 28 29 PARTE III. ACCESO A UN DOCUMENTO XML 3.1 Construcción de una aplicación JAXB 31 31 78 3.2 Creación del Esquema de Unión 3.2.1 Declaración de Unión de Elementos 3.2.2 Declaración de Unión de Atributos 3.2.3 Declaración de Unión de Contenido 3.2.4 Ejemplo: Esquema de Unión para book.dtd. 3.2.5 Especificando Tipos 3.2.5.1 Especificar Tipos No Primitivos 3.2.5.2 Especificar Tipos Primitivos 3.2.6 Creación de Tipos de Datos Enumerados 3.2.7 Creación de interfaces 32 33 34 35 37 38 38 40 41 42 3.3 Generación de los Ficheros Fuente de Java 43 3.4 Configuración de la aplicación JAXB 45 3.5 Construcción de Representaciones de Datos 3.5.1 Desempaquetar (Construcción del árbol de Contenido) 3.5.1.1 Ejemplarización 3.5.2 Validación 3.5.3 Trabajar con Datos (Acceso al árbol de Contenido) 45 46 47 49 49 3.6 Creación de Objetos de Java a partir de Esquemas XML 3.6.1 La Representación Java de Esquema XML 3.6.2 Archivos Básicos 3.6.3 Escribir un esquema de unión 3.6.4 Ejecutar el compilador de esquema 3.6.4.1 La configuración y la ejecución 3.6.4.2 Opciones de la compilación de JAXB 3.6.4.3 Acerca de los esquemas de unión para Java en la compilación 3.6.5 Ejemplo: Generar Clases desde un DTD 3.6.5.1 Construir Representaciones Objeto de Datos XML 50 50 51 51 51 52 52 53 54 55 3.7 Creación de Esquemas XML a partir de Objetos de Java. 3.7.1 Validación del árbol de Contenido 3.7.2 Empaquetar el árbol de Contenido a Documentos XML 56 57 58 PARTE IV. CREANDO UNA APLICACIÓN JAXB 60 4.1 Consideraciones Previas 60 4.2 EJEMPLO 1: Utilizando la operación unmarshal 60 4.3 EJEMPLO 2: Utilizando la operación marshal 67 4.4 EJEMPLO 3: Definiendo un árbol de contenido y transformarlo en formato XML 69 4.5 EJEMPLO 4: Generando un esquema XML a partir de clases java 71 ANEXOS Proceso de Instalación de JAXB 2.0 74 74 79