Interacción y manejo de documentos XML. Como último miembro de la familia XML, nos planteamos la tecnología por la cual una aplicación externa, escrita en no importa que lenguaje de programación, puede manejar un documento XML. Ello supone estandarizar las formas de interaccionar de un documento XML con otras piezas de software, al objeto de poder llevar a cabo determinadas tareas imprescindibles en el procesado de datos, y sin cuya garantía de compatibilidad y accesibilidad, no llegarían a satisfacerse los objetivos que se persiguen con XML. Manejo de documentos XML con una aplicación La arquitectura básica que gobierna la interacción, en ambos sentidos, entre una aplicación externa y un documento XML se muestra en la siguiente figura en la que entre una y otra aparecen una serie de elementos software. Aplicación Documento XML Cadena de caracteres Serializador Analizador APIS XML Estandarizados Como se observa en el proceso de proporcionar a la aplicación datos procedentes de documentos XML, el primer paso para que éstos sean accesibles es el análisis del propio documento, con el consiguiente proceso de descomposición en nodos y piezas identificables ( etiquetas, trozos de texto, IP, comentarios etc ) de tal manera que una vez hecho, se puedan integrar con la aplicación correspondiente, mediante una API ( Application Programming Interface ) debidamente estandarizada. Respecto al problema inverso, esto es, que la aplicación pueda generar documentos XML en su nivel más básico, siempre se puede pensar que la propia aplicación puede obtener como resultado directo un marcado XML, generando el correspondiente flujo de caracteres que produzca un documento. Sin embargo, aunque ello no sea difícil, si es evidentemente muy laborioso al tener que respetar todas y cada una de las reglas sintácticas del XML. Lo anterior conduce a que muy a menudo sea más fácil para la aplicación descomponer el proceso de obtención del documento en dos fases, la primera consistente en construir mediante una API, una estructura de datos en forma de árbol que describa el documento Juan Manuel Alcazar Donaire 1/9
a generar para a continuación en la segunda fase poner en marcha un proceso que obtenga el marcado XML buscado. Como se observa, mediante APIs se enfrentan dos problemas en principio distintos : por un lado escribir un documento XML ( serialización ) y por otro analizar los documentos. Al estar ambos procesos obviamente orientados a la sintaxis, se fuerza a la aplicación a trabajar teniendo en cuenta conceptos tales como elemento, atributo, segmentos de texto, etc., cosas en principio alejadas de la preocupación del autor de la aplicación, lo que le proporciona una inestimable ayuda a la hora de enfrentarse con documentos XML. Todo ello hace necesario que la interacción aplicación/documento quede lo más encapsulada posible y en consecuencia que incorpore los mayores niveles posibles de estandarización. En la práctica, las aplicaciones suelen trabajar con una visión poco detallada, adoptando una aproximación de alto nivel respecto a sus datos, de tal forma que tratan de abstraerse de los detalles sintácticos, limitándose a exponer el significado o la semántica de los datos manejados. En otras palabras, las aplicaciones interaccionan habitualmente con APIs orientadas a datos, por lo que se acaba introduciendo una capa de abstracción de datos entre las APIs orientadas a resolver los problemas de sintaxis XML y la lógica propia de la aplicación. Generación de documentos : Serialización Siempre una aplicación puede escribir directamente un marcado XML generado a partir del correspondiente flujo de caracteres, cosa laboriosa al tener que respetar las reglas sintácticas, algunas de ellas objeto de un particular cuidado : citación de atributos, supresión de caracteres especiales, etc., las aplicaciones se limitan a construir una estructura de datos en forma de árbol que describa el documento a generar, de tal manera que posteriormente un módulo distinto e independiente de la aplicación pueda recorrerlo para obtener el marcado correspondiente de sus elementos. El proceso anterior, conocido genéricamente en Informática como serialización, consiste en convertir los datos de la aplicación a un tipo de datos expresados secuencialmente, como es el caso de un documento XML, de forma que el resultado sea una representación de los primeros, en nuestro caso representados con sintaxis XML. El proceso inverso consiste en generar datos para la aplicación a partir de documentos XML, recibe el nombre deserialización. Tipos de APIs entre Documentos y Aplicaciones Para enlazar el resultado del análisis del documento con el posterior procesado de sus datos existen dos posibilidades : a) Aprovechar la estructura de árbol con la que el analizador lo representa en memoria para las posibles modificaciones del mismo ( añadir, borrar nodos, etc ) de forma parecida a como trabaja una Base de Datos, aunque ahora usando esta estructura de árbol. Juan Manuel Alcazar Donaire 2/9
b) Asumir que a medida que el documento se analiza, se generan una serie de notificaciones, llamados eventos, lo que permite tomar decisiones ante cada uno de ellos. En la primera posibilidad se basa la idea que produce la Recomendación W3C, DOM ( Document Object Model ), mientras que la segunda es la utilizada por SAX ( Simple API for XML ), desarrollado en 1998, de manera independiente al W3C. Aunque tanto DOM como SAX sean dos APIs que acceden a la información contenida en un documento XML, se observa que lo hacen de forma sensiblemente distinta. Por su lado, DOM se basa en una jerarquía de nodos en árbol con todos los datos del documento ubicados en memoria, cosa que permite acceder a ellos de forma inmediata, pudiendo, en consecuencia, añadir y borrar nodos, lo que supone tener la capacidad de modificar el documento de forma realmente sencilla antes de pasarlo a la aplicación. Mientras, por su parte SAX, en lugar de trabajar con la estructura global de árbol, se limita a detectar eventos, y a partir de ellos invocar métodos, de forma que los datos se pasan a la aplicación tal como la API los va encontrando en el documento; con ello se evita tener que disponer de la totalidad del árbol en memoria, bien es cierto que a costa de no cambiar realmente el documento original. Como puede verse, a la hora de elegir una u otra posibilidad, existe una negociación que implica a parámetros tales como las necesidades de memoria, la eficiencia computacional y las facilidades de programación. Se observa que SAX es más eficiente y ocupa menos memoria, mientras que DOM permite recorrer y manipular el documento con más facilidad; consecuencia de ello es que SAX se use normalmente para leer documentos que no tienen que ser modificados mientras que DOM tiene por parte de la W3C, y las crecientes posibilidades de los equipos de computación hagan que éste sea cada vez más usado como puente entre las aplicaciones y los documentos XML que aquellas utilicen. En cualquier caso es importante recalcar que ambas APIs juegan el papel de interfaz independiente del lenguaje y de la plataforma, permitiendo que programas y scripts accedan, analicen y actualicen dinámicamente el contenido, estructura y estilo de un documento; gracias a ello, tras el proceso, los resultados correspondientes pueden reincorporarse a la página de procedencia. En el mundo Java se usa una API estandarizada llamada Java API for XML Processing (JAXP) que además de instanciar los analizadores XML, analiza documentos usando SAX o DOM. Nótese que de hecho ( y esto vale para el resto de lenguajes de programación), si no se contara con JAXP, las aplicaciones Java no serían completamente portables a los distintos analizadores XML, porque cada uno de ellos, siguiendo SAX o DOM, tendrían diferentes APIs para crear, configurar y procesar documentos. Trabajar con DOM Juan Manuel Alcazar Donaire 3/9
Aunque DOM se puede utilizar para cualquier lenguaje, por razones obvias nos vamos a centrar en Java para introducir su forma de trabajar. En este sentido debe saberse que la comunidad Java trabaja sobre JDOM un módulo de JAXP, que como se ha dicho proporciona una herramienta de alto nivel para poder trabajar con XML. Para presentar los ejemplos, recurriremos a una serie de paquetes básicos ( incluiremos con el correspondiente import ) y que en estos momentos agrupan a una serie de ficheros relacionados entre sí, y de los cuales nos interesan especialmente : n org.w3c.dom ( la interfaz API de DOM para programar ) n javax.xml.parser ( con las clases para compilar ) La interfaz DOM describe una sintaxis de bajo nivel dirigida a la construcción y manejo de documentos XML que se puede usar a través de cualquier lenguaje de programación. Al tener como objetivo poder interactuar con los distintos nodos del árbol DOM, estas interfaz carecen de una implementación particular, ya que se especifican en IDL con lo que todo lenguaje puede definir enlaces para ellas. En un primera aproximación basta con entender el papel que juegan estas interfaces dentro de la metodología seguida por DOM, por ello la primera evidencia es que su relación debe coincidir básicamente con los tipos de nodos que admite un árbol DOM. DOMImplementation Document Node NodeList Element Attr CharacterData Text Comment ProcessingInstruction CDATASecction Proporcina métodos a nivel de Documento ( independientes de cualquier nodo específico ). Representa el nodo de nivel superior del documento que proporciona acceso a todos los nodos, incluido el raíz. Representa un nodo del documento XML. Representa a una lista de sólo lectura de objetos Node. Representa a un nodo elemento. Deriva de Node. Representa un nodo atributo. Deriva de Node. Representa caracteres de datos. Deriva de Node. Representa un nodo de texto. Deriva de CharacterData. Representa un nodo comentario. Deriva de CharacterData. Representa a un nodo de una instrucción de procesado. Deriva de Node. Representa una sección CDATA. Deriva de Text. //////////////////////////////////////////////////////////////// // Conversión de DOM a XMLData Function ProcessDOMNode(objNode,objCreator) var objroot; Juan Manuel Alcazar Donaire 4/9
objroot = CreateXMLDataFromDOMNode(objNode,objCreator); If(objRoot) If((objNode.nodeValue!= Null) && (objnode.nodevalue.length > 0)) objroot.textvalue = objnode.nodevalue; // add attributes If(objNode.attributes) var Attribute; var onodelist = objnode.attributes; For(var i = 0;i < onodelist.length; i++) Attribute = onodelist.item(i); var newnode; newnode = ProcessDOMNode(Attribute,objCreator); objroot.appendchild(newnode); If(objNode.hasChildNodes) try // add children var Item; onodelist = objnode.childnodes; For(var i = 0;i < onodelist.length; i++) Item = onodelist.item(i); var newnode; newnode = ProcessDOMNode(Item,objCreator); objroot.appendchild(newnode); Juan Manuel Alcazar Donaire 5/9
catch(err) Return objroot; Function CreateXMLDataFromDOMNode(objNode,objCreator) var bsetname = True; var bsetvalue = True; var nkind = 4; switch(objnode.nodetype) Case 2:nKind = 5;break; Case 3:nKind = 6;bSetName = False;break; Case 4:nKind = 7;bSetName = False;break; Case 8:nKind = 8;bSetName = False;break; Case 7:nKind = 9;break; var objnew = Null; objnew = objcreator.createchild(nkind); If(bSetName) objnew.name = objnode.nodename; If(bSetValue && (objnode.nodevalue!= Null)) Juan Manuel Alcazar Donaire 6/9
objnew.textvalue = objnode.nodevalue; Return objnew; //////////////////////////////////////////////////////////////// // XMLData To DOM conversion Function ProcessXMLDataNode(objXMLData,xmlDoc) var objroot; objroot = CreateDOMNodeFromXMLData(objXMLData,xmlDoc); If(objRoot) If(IsTextNodeEnabled(objRoot) && (objxmldata.textvalue.length > 0)) objroot.appendchild(xmldoc.createtextnode(objxmldata.textvalue)); If(objXMLData.HasChildren) try var objchild; objchild = objxmldata.getfirstchild(-1); While(True) If(objChild) var newnode; newnode = ProcessXMLDataNode(objChild,xmlDoc); If(newNode.nodeType == 2) // child node is an attribute objroot.attributes.setnameditem(newnode); Else Juan Manuel Alcazar Donaire 7/9
objroot.appendchild(newnode); objchild = objxmldata.getnextchild(); catch(err) Return objroot; Function CreateDOMNodeFromXMLData(objXMLData,xmlDoc) switch(objxmldata.kind) Case 4:Return xmldoc.createelement(objxmldata.name); Case 5:Return xmldoc.createattribute(objxmldata.name); Case 6:Return xmldoc.createtextnode(objxmldata.textvalue); Case 7:Return xmldoc.createcdatasection(objxmldata.textvalue); Case 8:Return xmldoc.createcomment(objxmldata.textvalue); Case 9:Return xmldoc.createprocessinginstruction(objxmldata.name,objxm LData.TextValue); Return xmldoc.createelement(objxmldata.name); Function IsTextNodeEnabled(objNode) Juan Manuel Alcazar Donaire 8/9
switch(objnode.nodetype) Case 1: Case 2: Case 5: Case 6: Case 11:Return True; Return False; Juan Manuel Alcazar Donaire 9/9