XML y Revolution

Examinaremos en este apartado las recientes incorporaciones de funciones que permiten el acceso a los contenidos de un documento XML y sus posibilidades de procesado. Estas aparecieron de forma independiente en base al trabajo de SonOfThunder (XMLLibrary) y que pudimos ver empleadas de forma gráfica en el control mejorado de Altuit (XML TreeView List Control).

Para trabajar con un fichero XML se puede hacer directamente accediendo a sus contenidos: puesto que es un fichero de texto, no representa mayores problemas. A partir de aquí se pueden utilizar las funciones de búsqueda de texto tradicionalmente incluídas en MetaCard o Revolution. Pero, desde la perspectiva de XML, un documento es una estructuración jerárquica de sus contenidos. Este concepto permite definir un modelo del documento que suele expresarse en términos de un árbol, de esta forma el procesado de un documento se convierte en las posibles secuencias de recorrido sobre el mismo en las que se irán (posiblemente) realizando operaciones de consulta, edición y borrado.

Así que a partir de ahora, nos referiremos al contenido de un documento como una serie de nodos organizados de forma jerárquica [1] . Estos nodos podrán estar vacios o no. Dentro de los segundos se podrán encontrar diferentes combinaciones de: contenido del nodo, atributos de la etiqueta que lo define y, posiblemente, más nodos. En este sentido, la presente librería nos ofrece funciones e instrucciones para procesar estos objetos y que podemos agrupar en:

  1. Operaciones con ficheros que contienen o contendrán documentos XML, como por ejemplo revXMLText y revCreateXMLTreeFromFile (?).

  2. Creación de árboles y liberación de los recursos asignados a estos, como son revXMLTree, revXMLTrees y revCreateXMLTree.

  3. Recorrido de árbol en forma absoluta desde la raiz, relativa desde un nodo o en función de la relación padre-hijo existentes. En este grupo encontraremos revXMLNumberOfChildren, revXMLParent revXMLNextSibling, revXMLPreviousSibling.

  4. Consulta y modificación de nodos, por ejemplo, revXMLRootNode, revXMLNodeContents, revAddXMLNode, revXMLDeleteNode, revXMLPutIntoNode, revXMLChildContents, revXMLChildName, revXMLFirstChild y revXMLMAtchingNode.

  5. Consulta y modificación de atributos de un elemento, entre las que se encuentran revXMLAttribute, revXMLAttributeValues y revXMLAttributeValue.

  6. Manejo de DTDs y validación de documentos XML, como revXMLAddDTD y ....

Figura 7-10. Pila para la visualización de las funciones sobre tratamiento de documentos XML en Revolution.

Figura 7-11. Ejemplo de fichero XML: animals.xml.

<?xml version="1.0"?>
<endangered_species>
 <animal>
  <name language="english">Tiger</name>
  <name language="latin">Panthera tigris</name>
  <threat>poachers</threat>
  <weight>500</weight>
 </animal>
 <animal>
  <name language="english">Kitty</name>
  <threat>My Dog!</threat>
 </animal>
 <animal>
  <name language="english">dog</name>
  <threat>cat</threat>
 </animal>
</endangered_species>
                      

Para exponerlas se van a ir desglosando las piezas que forman un pequeño tutorial visual (del cual se puede ver su apariencia en la Figura 7-10) acerca de qué ocurre tras ejecutar cada una de las funciones indicadas. En las siguientes figuras se mostrarán los resultados obtenidos al procesar un mismo fichero XML. El contenido [2] del fichero animals.xml que acompaña a la distribución de Revolution y que se muestra en la Figura 7-11, junto al DTD utilizado.

Figura 7-12. Ejemplo de fichero DTD: animals.dtd. Una pequeña nota: significa "en peligro" (de extinción, en este caso), threat significa amenaza y poachers son "cazadores furtivos".

<! ELEMENT endangered_species (animal+)>
<!ELEMENT animal (name+, threat, weight)>
<!ELEMENT name (#PCDATA)>
<!ATTLIST name
                         language (english | latin ) #REQUIERED>
<!ELEMENT threat  (#PCDATA)>
<!ELEMENT weight (#PCDATA)>
                      

Pero, para que sea eficiente, es conveniente utilizar las funciones incorporadas a partir de la versión 2.5 de MetaCard o la 2.0 de Revolution. En los siguientes puntos se irán desgranando estas funciones de un manera sucinta, ofreciendo de esta manera una sencilla aproximación a la labor y posibilidades de cada una de estas funciones.

Nota: La salida de todas las secuencias de código se mostrará en un campo de texo adyacente al botón que las inicia para poder comprobar el resultado devuelto.

Todas las funciones pueden devolver un código de error (una cadena de texto que empieza con 'xmlerr'), así que es interesante comprobar el resultado que devuelven tras su ejecución. Aquí, no está hecho para centrar la atención del lector en las funciones y sus parámetros, minimizando el número de instrucciones empleadas. Pero, de forma general se puede utilizar la secuencia siguiente:

 put nombreFuncion( parametro1, .., parametroN) 
 if char 1 to 6 of it is "xmlerr" \
      then\
          answer error "Se ha producido un error al ejecutar" & return &\
                       "nombreFuncion( parametro1, .., parametroN) " & return &\
                        title "[" & the short name of me & "]"
    
                    

Abrir un documento XML

Para cargar un fichero XML se puede leer directamente del disco como un fichero más, pero para empezar a trabajar con él de una forma que permita su proceso hay que crear una estructura en forma de árbol que haga eficiente su recorrido.

La función revCreateXMLTree(XMLText,parseBadData,createTree,sendMessages) devuelve un valor númerico que es utilizado para referirse al árbol que ha creado, con las particularidades que indican sus parámetros.

En nuestro ejemplo, se ha concretado en la siguiente forma:

# revCreateXMLTree
on mouseUp
 put revCreateXMLTree(field "fitxerXML","false", true, false) into fld "docID"
end mouseUp
                    

Que obtiene el contenido XML no directamente de un fichero, si no que lo toma del campo de texto indicado (

field "fitxerXML"
).

Y en el campo de texto indicado para devolver el resultado (

field "docID"
), aparece un valor numérico indicando que la operacion ha terminado de manera satisfactoria. En caso de error se vería la cadena correspondiente empezando con, como ya se ha dicho, la palabra xmlerr. En nuestro caso, este valor es el que utilizaremos en las posteriores funciones para referirnos a la representación interna que se acaba de crear con esta función.

Acceso al nodo raiz de un documento XML

Si ya se ha accedido al contenido del fichero y se ha creado la estructura de árbol, podemos empezar a pensar en recorrerlo y procesarlo: extrayendo la información del mismo y modificándola o simplemente visualizando. En este caso es interesante hacerlo por el principio: el nodo raiz.

Para ete propósito, la función revXMLRootNode(treeID) devuelve la situación de este nodo único, a partir de la indicación de un árbol ya creado.

En nuestro ejemplo, se ha concretado en la siguiente forma:

on mouseUp
  put revXMLRootNode(fld "docID" ) into fld "nodeRail"
end mouseUp
                    

Aviso

En el caso de un fichero con cabecera (el anterior no més que té lo de ?xml="1.0" ...) ??? Què pasa en eixe cas?.

Hijos de un nodo XML

A partir de la localización de un nodo del árbol es posible seguir descendiendo en el mismo consultando los nodos hijos de aquel. La función revXMLFirstChild(treeID,parentNode) devuelve la situación en el árbol del primero de los hijos de un nodo dad. Asó por ejemplo para obtener el primero de los hijos del nodo raiz se puede llevar a cabo de la forma siguiente:

on mouseUp
  put revXMLFirstChild(fld "docID",  fld "nodeRail") into fld "primerFill"
end mouseUp

                    

Observese que se utilizan los valores guardados en los campos de texto que se van incluyendo para visualizar los valores obtenidos en cada paso. En este caso: el identificador del árbol y la situación del nodo raiz.

Nodos al mismo nivel de un documento XML

Para recorrer los nodos hermanos de uno dado, esto es situados en un punto cualquiera del árbol moverse entre los nodos que están al mismo nivel de profundidad respecto del nodo raiz, se utilizan dos funciones en función del sentido en que se desee realizar la exploración: a la derecha (el nodo hermano siguiente) o a la izquierda (el anterior)..

La función revXMLNextSibling(XMLText,siblingNode) permite obtener la situación del nodo hermano anterior como se muestra en el siguiente fragmento de cçodigo.

on mouseUp
  put revXMLNextSibling(fld "docID",  fld "primerFill" ) into fld "seguentFill"
end mouseUp
                      

Análogamente a la anterior, para moverse en el otro sentido se utiliza la función revXMLPreviousSibling(XMLText,siblingNode) que devuelve una cadena de caracteres donde se describe la situación de este nodo hermano.

En nuestro ejemplo, se ha concretado en la siguiente forma:

on mouseUp
  put revXMLPreviousSibling(fld "docID",  fld "seguentFill" ) into fld "fillAnterior"
end mouseUp
                     

Nodo padre de otro en un documento XML

Si en el recorrido del árbol se hace necesario subir un nivel, en cualquier nodo del mismo, se ha de hacer uso de la función revXMLParent(XMLText,childNode) que obtiene la situación del nodo padre, respecto al nodo hijo indicado. Así lo muestra este código:

on mouseUp
  put revXMLParent(fld "docID",  fld "seguentFill") into fld "nodePare"
end mouseUp
                      

En el caso del nodo raiz, se devuelve la cadena vacía.

Atributos de un nodo

Para ...

La función revXMLAttribute(XMLText,parseBadData,createTree,sendMessages) devuelve ...

En nuestro ejemplo, se ha concretado en la siguiente forma:

on mouseUp
  put fld "primerFill" & "/name [language] ="&& \
        revXMLAttribute( fld "docID",  fld "primerFill" & "/name", "language" ) into \
       fld "atribut"
end mouseUp
                    

La función revXMLAttributes(XMLText,parseBadData,createTree,sendMessages) devuelve un valor númerico que es utilizado para referirse al árbol que ha creado, con las particularidades que indican sus parámetros.

on mouseUp
  put empty into atributs
  
  repeat for each line thisLine in \
           revXMLAttributes( fld "docID",  fld "primerFill" & "/name", "=" & quote, quote ) 
    put thisLine & ", "  after atributs
  end repeat

  put ("Atributs de " & fld "primerFill" & "/name:" & return & atributs) into fld "atributs"
  put ("Atributs de " & fld "primerFill" & "/name  son:" & return & atributs)
end mouseUp
                    

Manejo de árboles y XML

Hemos hablado que los documentos XML se guardan internamente como árboles para hacer posible un recorrido eficiente y de cómo estos árboles se nombran mediante identificadores (numéricos) que asignan las llamadas que utilizamos. En este apartado se exponen las que hacen posible averiguar cuáles hay y liberar esos identificadores cuando ya no son necesarios.

La función revXMLTrees(XMLText,parseBadData,createTree,sendMessages) devuelve un valor númerico que es utilizado para referirse al árbol que ha creado, con las particularidades que indican sus parámetros.

En nuestro ejemplo, se ha concretado en la siguiente forma:

on mouseUp
  put empty into atributs
  
  repeat for each line thisLine in revXMLTrees()
    put thisLine & ", "  after atributs
  end repeat

  put ("Arbres actius:" & return & atributs) into fld "deleteXMLTree"
end mouseUp
                    

La función revDeleteXMLTree(XMLText,parseBadData,createTree,sendMessages) devuelve un valor númerico que es utilizado para referirse al árbol que ha creado, con las particularidades que indican sus parámetros.

on mouseUp
  revDeleteXMLTree fld "docID"
  put the result into fld "deleteXMLTree"
end mouseUp

                    

La orden revDeleteAllXMLTrees ???.

on mouseUp
   revDeleteAllXMLTrees
  put the result into fld "deleteXMLTree"
end mouseUp

Removes all XML tree structures in memory.
revDeleteAllXMLTrees

                    

Hijos de un nodo

Para ...

La función revXMLChildNames(XMLText,parseBadData,createTree,sendMessages) devuelve ...

En nuestro ejemplo, se ha concretado en la siguiente forma:

on mouseUp
     put revXMLChildNames( fld "docID",  "/", space, empty, true ) &\
           return &\
           revXMLChildNames( fld "docID",  "/", space, empty, false )  into fld "nomFills"
end mouseUp

                    

La función revXMLChildContents(XMLText,parseBadData,createTree,sendMessages) devuelve ...

En nuestro ejemplo, se ha concretado en la siguiente forma:

on mouseUp
    put revXMLChildContents( fld "docID",  fld "primerFill", space, space, false, 0 ) 
    put revXMLChildContents( fld "docID",  fld "primerFill", space, space, true, 0 )  into \
           fld "contingutFills"
end mouseUp


                    

Nodos como entidades en XML

Para ...

La función revXMLNodeContents(XMLText,parseBadData,createTree,sendMessages) devuelve ...

En nuestro ejemplo, se ha concretado en la siguiente forma:

on mouseUp
     put revXMLNodeContents( fld "docID",  fld "primerFill" & "/name" )  into \
           fld "contingutFills"
end mouseUp

                    

Notas

[1]

Como la estructura de un sistema de ficheros, de hecho utilizaremos la nomenclatura más extendida en este campo para nombrar los nodos que lo conforman.

[2]

Una pequeña nota al respecto de los términos utilizados como etiquetas: endangered_species significa "en peligro" (de extinción, en este caso), threat significa amenaza y poachers son "cazadores furtivos"