|
Buscador
Secciones
Registro
¡Colabora!
Ganamos
Servicios
|
Inicio > Tutoriales > Lenguajes orientados a objeto > Java > Java y XML > Integración de XML y los JavaBeans
Figura 1: Métodos XMLBeanWriter.writeXMLBean() La primera versión del método (líneas 287 a 290) toma un nombre de fichero String, luego simplemente crea un objeto File, y lo pasa a la tercera versión del método (líneas 325 a 329) que usa su argumento File para construir un FileWriter, y pasar el resultado a la segunda versión. Es la segunda versión de writeXMLBean (líneas 302 a 317) la que realmente hace el trabajo. Esta clase writeXMLBean podría muy fácilmente escribir su XML al Writer, siguiendo la pista a la identación etc. Pero pensemos en esto por un momento: un documento XML en un programa se puede representar como un árbol de nodos del modelo de objeto del documento (DOM), ¿correcto? Y deseamos aplanar este árbol de nodos DOM y escribirlo a un fichero de texto. Bien, ¿por qué no tener un método que construya realmente el árbol DOM que representa el JavaBean, y entonces otro que imprima el árbol DOM como XML? Podríamos exponer los métodos (que verémos abajo) que convierten un JavaBean en un árbol, y después convertimos el árbol a texto, que entonces se escribe a un fichero. Por ejemplo, imaginemos que tenemos un JavaBean que se parece al de la figura 2. ![]() Figura 2: Un árbol de JavaBean El JavaBean de la clase Grade tiene dos propiedades: un float llamado "promedio", y un JavaBean de la clase Course llamado "curso". (recuerda que el valor de una propiedad JavaBean puede en sí mismo ser un JavaBean.) La clase XMLBeanWriter internamente construirá un árbol DOM para este JavaBean que se parecerá a la figrua 3. ![]() Figura 3: Un árbol de DOM que representa nuestro JavaBean. El XML correspondiente al árbol de la Figura 3 aparece en la figura 4:
Figura 4: XML del Árbol DOM. Hay tres excelentes razones para construir un árbol y luego imprimirlo, en lugar de sólo imprimir XML mientras analizamos el JavaBean.
Ahora que tenemos una idea de qué es lo que hace el código, echemos un vistazo al propio código fuente. El codigo de todas las versioens de XMLBeanWriter aparece abajo en la Figura 5: Volviendo a la tercera versión de writeXMLBean, observa que primero creamos un TXDocument llamado doc, como este:
Un TXDocument es una implementación específica del interface org.w3c.dom.Document (o, sólo Document). Document es sólo un interface definido por el W3C. No tiene ninguna implementación determinada, pero ha definido el comportamiento, que resume qué es un interface. TXDocument es una clase del paquete xml4j de IBM que implementa el interface Document. Es la raíz del árbol DOM del documento . El interface Document (puedes leer sobre él en http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#i-Document) define un API que permite que un programador agregue, suprima, e itere sobre los elementos hijos del árbol DOM del documento. Después de haber creado el Document que vamos a imprimir, llamamos al método getAsDOM(), que construye un árbol DOM de documento basándose en las propiedades del JavaBean.
Aquí hay dos cosas interesantes. La primera es que getAsDOM() devuelve un DocumentFragment, que requiere muy poca explicación. ¿Recuerdas de antes, donde explicamos el recorte y pegado de piezas de árboles del documento? Bien, un DocumentFragment es sólo lo que su nombre implica: es un pequeño pedazo de un documento que se pueda agregar a la lista de hijos de cualquier Node DOM. Un DocumentFragment sea un contenedor de peso ligero que, cuando es agregado a los Nodes hijos de un DOM le da todos sus hijos a ése Node, pero no aparece como un hijo del propio Node. Así pues, en la segunda línea de arriba, cuando se añade el DocumentFragment al final de la lista de hijos del Document, el Document no tiene un hijo que sea DocumentFragment; en vez de eso, tiene cualquier hijo que tuviera el DocumentFragment. (El DocumentFragment permanece sin cambiar: "no pierde" a sus hijos Document.) Nosotros utilizamos los objetos DocumentFragment extensivamente en WriteXMLBean. La segunda cosa interesante sobre la llamada a getAsDOM() es que estamos pasando el objeto Document al método. Hacemos esto por una razón muy específica: los únicos objetos que se pueden agregar a un objeto Document son los objetos que el mismo Document ha creado. Observaremos que hace algunas líneas, creamos un ejemplar de TXDocument. Esta es una implementación específica de un objeto DOM implementado por IBM. Los otros objetos que pueden aparecer en un árbol DOM -- Comment, Element, Text, etc. -- también deben ser creados. En vez de las declaraciones new() por todas partes, cada de las cuales tendría que ser modificada si las clases que implementaban el DOM se modificaran, Document define una lista de métodos que realizan la creación de los objetos secundarios del árbol, sobre peticiones. Así pues, si quisieramos que un objeto del nodo Text agregara algún nodo al árbol DOM que estámos construyendo, en vez de decir:
le pediríamos al Document que creara uno por nosotros:
Los otros tipos de objetos DOM se crean de forma similar, veremos ejemplos más abajo. Los métodos create del interface Document son un excelente ejemplo del modelo de diseño Factory, donde la creación de un objeto se difiere a otro objeto. Si cambiamos a otra implementación de DOM, sólo tendremos que cambiar el new(TXDocument). Todos los otros objetos se crearán desde la nueva clase Document. El uso de este patrón de diseño de Factorías proporciona otros beneficios que son poderosos pero se salen del ámbito de este tutorial. La línea final del método writeXMLBean llama al método sobre el que hablamos antes, en el que el objeto TXDocument escribe su árbol en el Writer en formato XML:
Ya hemos visto el marco de trabajo , ahora veamos cómo getAsDOM() crea un árbol DOM.
|
032 public static DocumentFragment getAsDOM(Document doc, Object bean)
throws IntrospectionException, InstantiationException, IllegalAccessException {
033
034 // Create the fragment we'll return.
035 DocumentFragment dfResult = null;
036
037 // Analyze the bean.
038 Class classOfBean = bean.getClass();
039
040 // If the bean knows how to encode itself in XML, then
041 // use the DOM document it returns.
042 try {
043 Method mgetAsDOM = classOfBean.getMethod("getAsDOM",
044 new Class[] { org.w3c.dom.Document.class } );
045 dfResult = (DocumentFragment) mgetAsDOM.invoke(bean,
046 new Object[] { doc });
047 } catch (Exception e) { 048;; // Ignore exceptions
049 }
050
051 // If the bean doesn't know how to encode itself in XML,
052 // then create a DOM document by introspecting it.
053 if (dfResult == null) {
054 dfResult = doc.createDocumentFragment();
055 BeanInfo bi = Introspector.getBeanInfo(classOfBean);
056 PropertyDescriptor[] pds = bi.getPropertyDescriptors();
057
058 // Add an Element indicating that this is a JavaBean.
059 // The element has a single attribute, which is that Element's class.
060 Element eBean = doc.createElement("JavaBean");
061 dfResult.appendChild(eBean);
062 eBean.setAttribute("CLASS", classOfBean.getName());
063 Element eProperties = doc.createElement("Properties");
064 eBean.appendChild(eProperties);
065
066// For each property of the bean, get a DocumentFragment that
067// represents the individual property. Append that DocumentFragment
068// to the Properties element of the document
069 for (int i = 0; i < pds.length; i++) {
070 PropertyDescriptor pd = pds[i];
071 DocumentFragment df = getAsDOM(doc, bean, pd);
072 if (df != null) {
073 // Create a Property element and add to Properties element
074 Element eProperty = doc.createElement("Property");
075 eProperties.appendChild(eProperty);
076
077 // Create NAME attribute, add it to Property element,
078 // and set it to name of property
079 eProperty.setAttribute("NAME", pd.getName());
080
081 // Append the DocumentFragment to the Property element
082 // This "splices" the entire DOM representation of the
083 // Property into the tree at this point.
084 eProperty.appendChild(df);
085 }
086 }
087 }
088 return dfResult;
089 }
|
Lo primero que hace getAsDOM(), a excepción de obtener la clase Bean para su uso posterior, es controlar para ver si el bean define un método llamado DocumentFragment getAsDOM(org.w3c.Document) (líneas 42 a 49). Si es así invoca a ese método en el Bean, y selecciona el DocumentFragment resultante (el subdocumento que estamos construyendo) a lo que ese método devuelve. Hace esto para permitir que un desarrollador reemplace la forma estándar de representar este ejemplar del objeto como XML. Éste es una convención que writeXMLBean define para agregar flexibilidad.
Esencialmente, WriteXMLBean.getAsDOM() le está "pidiendo" al JavaBean, "hey, Tu! ¿sabes representarte como un documento DOM?" La respuesta es "sí" si la clase del Bean define este método. Esto hace a XMLBeanWriter más flexible. Por ejemplo, si estámos escribiendo un JavaBean que deseamos convertir en XML, pero deseamos un cierto control sobre cómo ese Bean se representa en XML (digamos que no te gusta como lo hemos hecho), todavía podemos utilizar XMLBeanWriter. Simplemente definimos nuestro propio método getAsDOM() en nuestra clase JavaBean, y XMLBeanWriter.getAsDOM() devuelve cualquier DocumentFragment que devolaamos. Veremos un ejemplo de esto de nuevo en la sección Representar como XML .
Como ejemplo, hemos modificado la clase Player.java para definir su propio getAsDOM(). Digamos que el hipotético Bean del jugador de baseball (en Player.java) de nuestro ejemplo de la página anterior con la propiedad gradePointAverage, pero por razones de privacidad, queremos excluir este número del documento DOM resultante. El código aparece en la Figura 7:
024 public DocumentFragment getAsDOM(Document doc) {
025 DocumentFragment df = doc.createDocumentFragment();
026
027 // Create the entire document for the Bean in code.
028
029 Element eJavaBean = doc.createElement("JavaBean");
030 eJavaBean.setAttribute("CLASS", "Player");
031 Comment comment = doc.createComment("XML for this Player created by Player.getAsDOM()");
032 eJavaBean.appendChild(comment);
033 Element eProperties = doc.createElement("Properties");
034 eJavaBean.appendChild(eProperties);
035 Element eProperty;
036 eProperty = doc.createElement("Property");
037 eProperty.setAttribute("NAME", "highSchool");
038 eProperty.appendChild(doc.createTextNode(getHighSchool()));
039 eProperties.appendChild(eProperty);
040 eProperty = doc.createElement("Property");
041 eProperty.setAttribute("NAME", "number");
042 eProperty.appendChild(doc.createTextNode(Integer.toString(getNumber())));
043 eProperties.appendChild(eProperty);
044 eProperty = doc.createElement("Property");
045 eProperty.setAttribute("NAME", "name");
046 PersonName pnName = getName();
047 if (pnName != null) {
048 DocumentFragment dfName = pnName.getAsDOM(doc);
049 eProperty.appendChild(dfName);
050 eProperties.appendChild(eProperty);
051 }
052 eProperty = doc.createElement("Property");
053 eProperty.setAttribute("NAME", "stats");
054 try {
055 DocumentFragment dfStats = com.javaworld.JavaBeans.XMLBeans.XMLBeanWriter.getAsDOM(doc,
(Object)(getStats()));
056 eProperty.appendChild(dfStats);
057 eProperties.appendChild(eProperty);
058 } catch (Exception ee) {
059 // If an exception occurs, the property is simply ignored.
060 }
061 df.appendChild(eJavaBean);
062 return df;
063 }
|
En la figura 7, el nuevo método getAsDOM() realmente construye un subdocumento DOM en el código, y lo devuelve como un DocumentFragment. Observa que ignora la propiedad gradePointAverage que el mecanismo estándar habría hecho salir. También llama a PersonName.getAsDOM(). El XML que resulta de imprimir el árbol del documento aparece en la Figura 8 abajo. (Bean1.xml, es la entrada de esta ejecución.)
<JavaBean CLASS="Player">
<!--XML for this Player created by Player.getAsDOM()-->
<Properties>
<Property NAME="highSchool">Eaton</Property>
<Property NAME="number">12</Property>
<Property NAME="name">
<!--XML for this PersonName created by PersonName.getAsDOM()-->
First:
Jonas, Last:
Grumby
</Property>
<Property
NAME="stats">
<JavaBean CLASS="Statistics">
<Properties>
<Property NAME="year">1997</Property>
<Property NAME="atBats">69</Property>
<Property NAME="hits">30</Property>
<Property NAME="homeRuns">2</Property>
<Property NAME="runsBattedIn">15</Property>
<Property NAME="runs">31</Property>
</Properties>
</JavaBean>
</Property>
</Properties>
</JavaBean>
|
Observamos cómo el comentario incluido en Player.getAsDOM() dio lugar a un comentario en la salida. Observamos también la ausencia de cualquier promedio . Esta técnica es de gran alcance porque le da al programador que usa el XMLBeanWriter un completo control sobre la estructura de la salida. Una forma más elegante de hacer esto, implicando PropertyDescriptors, aparecerá en la página siguiente
Volvamos de nuevo a mirar el código. Refiriendonos a la Figura 5 otra vez, las líneas 54 a 55 hacen que el Bean consiga su objeto BeanInfo, y solicitándole la lista de objetos PropertyDescriptor del bean. En este punto, el programa conoce qué propiedades va a agregar al Document.
Las líneas 58 a 64 son nuestro primer ejemplo de creacción de objetos DOM y añadirlos al árbol:
058 // Add an Element indicating that this is a JavaBean.
059 // The element has a single attribute, which is that Element's class.
060 Element eBean = doc.createElement("JavaBean");
061 dfResult.appendChild(eBean);
062 eBean.setAttribute("CLASS", classOfBean.getName());
063 Element eProperties = doc.createElement("Properties");
064 eBean.appendChild(eProperties);
|
La línea 60 crea un elemento que se parece a <JavaBean> en XML. La siguiente línea (61) la agrega al árbol resultante. Entonces el método fija el atributo CLASS del elemento JavaBean al nombre de la clase del Bean (línea 62), así que el XML que resulta parecería <JavaBean CLASS="classname">. Las dos líneas siguientes (63 a 64) crean un elemento <Properties> y lo añaden al elemento <JavaBean>. En este punto, el árbol se parece a esto:
<JavaBean CLASS="classname"> <Properties> </Properties> </JavaBean> |
Por supuesto, el documento "realmente" no se parece a esto, En realidad, esto sólo una estructura de datos. El XML de arriba es lo que veríamos si imprimieramos el fragmento de documento en este punto.
El bucle de la parte inferior del método (líneas 69-86) itera sobre la lista de descriptores de propiedades, y por cada propiedad:
La estructura resultante es la representación DOM del JavaBean. Ahora todo lo que nos falta es entender cómo se representa una propiedad como XML.
Representar propiedades como XMLEl código para la segunda versión de getAsDOM() aparece en la figura 9. Este método genera el XML para la propiedad JavaBean indicada por el tercer argumento del método, un PropertyDescriptor. Este método podría devolver null, para indicar que no puede imaginarse cómo representar la propiedad como XML.
102public static DocumentFragment getAsDOM(Document doc, Object bean, PropertyDescriptor pd)
103 throws IntrospectionException, InstantiationException, IllegalAccessException {
104 Class classOfBean = bean.getClass();
105 Class classOfProperty = pd.getPropertyType();
106 DocumentFragment dfResult = null;
107 String sValueAsText = null;
108 Class[] paramsNone = {};
109 Object[] argsNone = {};
110
111 // If the property is "class", and the type is java.lang.class, then
112 // this is the class of the bean, which we've already encoded.
113 // So, in this special case, return null.
114 if (pd.getName().equals("class") && classOfProperty.equals(java.lang.Class.class)) {
115 return null;
116 }
117
118 // 1. Try to represent the property as XML.
119 // This bean may know how to describe itself, or parts of
120 // itself, as XML. There are two possibilities:
121 // [a] The bean has a method called get<Propname>AsXML()
122 // [b] The property class has a method called getAsDOM()
123 // We'll try both of these, and the first (if any) that
124 // works will be the DocumentFragment we want to return.
125 // If none of these are true, then we try to find the object's
126 // value as text.
127
128 // [1a] Does the bean have a method called get<Propname>AsXML()?
129 // Capitalize property name.
130 StringBuffer sPropname = new StringBuffer(pd.getName());
131 char c = sPropname.charAt(0);
132 if (c >= 'a' && c <= 'z') {
133 c += 'A' - 'a';
134 }
135 sPropname.setCharAt(0, c);
136 String sXMLGetterName = "get" + sPropname + "AsXML";
137
138 // If both of these methods succeed, then dfResult will be set
139 // to non-null; that is, the method existed and returned a
140 // DocumentFragment.
141 try {
142 Class [] params = { org.w3c.dom.Document.class };
143 Method mXMLGetter = classOfBean.getMethod(sXMLGetterName, params);
144 Object[] args = { doc };
145 dfResult = (DocumentFragment) (mXMLGetter.invoke(bean, args));
146 } catch (Exception ee) {
147 ;// Ignore... couldn't get the method
148 }
149
150 // Hereafter, we're trying to create a representation of the property
151 // based somehow on the property's value.
152 // The very first thing we need to do is get the value of the
153 // property as an object. If we can't do that, we can get no
154 // representation of the property at all.
155 Object oPropertyValue = null;
156 try {
157 Method getter = pd.getReadMethod();
158 if (getter != null) {
159 oPropertyValue = getter.invoke(bean, argsNone);
160 }
161 } catch (InvocationTargetException ex) {
162 ; // Couldn't get value. Probably should be an error.
163 }
164
165 // [1b] If we don't have a DocumentFragment, the previous block failed.
166 // So, let's find out if the property's class has a method called
167 // getAsDOM() and, if it does, call that instead.
168 if (dfResult == null) {
169 try {
170 Class [] params = { org.w3c.dom.Document.class };
171 Method mXMLGetter = classOfProperty.getMethod("getAsDOM", params);
172 Object[] args = { doc };
173 dfResult = (DocumentFragment) (mXMLGetter.invoke(oPropertyValue, args));
174 } catch (Exception ee) {
175 ; // Ignore -- who cares why it failed?
176 }
177 }
178
179
180 // 2. Try to represent the property as a String.
181 // See if this property's value
182 // is something we can represent as Text, or if it's something
183 // that must be represented as a JavaBean. Let's assume that this
184 // object can be represented as text if:
185 // [a] it has a PropertyEditor associated with it, because
186 // PropertyEditors always have setAsText() and getAsText().
187 // If it can't be represented as text, then we pass it to
188 // getAsDOM(Document, Object) and return the result.
189
190 if (dfResult == null) {
191
192 // [2a] Can we get either a custom or built-in property editor?
193 // If the PropertyDescriptor returns an editor class, we
194 // create an instance of it; otherwise, we ask the system for
195 // a default editor for that class.
196 Class pedClass = pd.getPropertyEditorClass();
197 PropertyEditor propEditor = null;
198 if (pedClass != null) {
199 propEditor = (PropertyEditor) (pedClass.newInstance());
200 } else {
201 propEditor = PropertyEditorManager.findEditor(classOfProperty);
202 }
203
204 // If the property editor's not null, pass the property's
205 // value to the PropertyEditor, and then ask the PropertyEditor
206 // for a text representation of the object.
207 if (propEditor != null) {
208 propEditor.setValue(oPropertyValue);
209 sValueAsText = propEditor.getAsText();
210 }
211
212 // If somewhere above we found a string value, then create
213 // a DocumentFragment to return, and append to it
214 // a Text element.
215 if (sValueAsText != null) {
216 dfResult = doc.createDocumentFragment();
217 Text textValue = doc.createTextNode(sValueAsText);
218 dfResult.appendChild(textValue);
219 }
220 }
221
222 // 3. Try to represent the property value as a JavaBean.
223 // If we don't have a DocumentFragment yet, we'll
224 // have to introspect the value of the object, because
225 // it's apparently something that can't be represented
226 // as flat text. We'll assume it's a JavaBean.
227 // If it isn't... oh, well.
228 //
229 if (dfResult == null) {
230 dfResult = getAsDOM(doc, oPropertyValue);
231 }
232 return dfResult;
233 }
|
Vayamos a través de esta segunda versión del método getAsDOM() y veamos que hace:
Conclusión En esta página, hemos creado una clase que convierte un JavaBean en XML. En la siguiente página, miraremos (y corregiremos) algunos de los problemas que nuestro nuevo código causará en XMLBeanReader.
| Leer comentarios (15) | |
| Escribir comentario | |
| Puntuación: |
|
| Votar | |
| Recomendar este tutorial | |
| Estadísticas |
Copyright © 1999-2006
Programación en castellano.
Todos los derechos reservados.
Formulario de Contacto -
Datos legales -
Publicidad
Hospedaje web y servidores dedicados linux por
Ferca Network