Aqui les dejo una serie de articulos que estoy poniendo en las news de Olivares2000, espero que les sea de utilidad, sobre el manejo de XML bajo xHarbour usando las clases que este compilador nos proveee.
Falta el ultimo paso que es aprender a guardar, que espero tenerlo lo
antes posible.
Bueno, como tengo bloqueado el acceso a mi blog, voy a ir poniendo aquí lo que he estado investigando sobre XML, gracias en parte a la documentación de Fredy, me a donado código donde poder observar 'como' se abre un fichero y su posterior grabación.
Por favor, si este documento contiene erratas, no duden en señalar donde metí la gamba.
En xHarbour, existen las clases TXmlDocument y compañia, realizadas por Giancarlo Niccolai, una gran persona y un grandísimo programador, que donó mucha parte de su tiempo a nuestro pequeña comunidad.
Bueno vamos allá. ( Ah, me recuerda a mis tiempos de Mini-FAQ )
1.- Como abrir un fichero XML y poder procesarlo.
He intentado realizar según lo que dice Fredy, usando el method Read(), que teóricamente, según entendí:
oXml := TXmlDocument():New()
oXml:Read( "fichero.xml" )
Pero, no se si esto esta bien, pues no consigo recorrer los nodos.
Asi, que , viendo código de Giancarlo, y después el de Ignacio, llegamos a la conclusión: ( A mi esto no me gusta... pero en fin.. )
hFile := FOpen( "reservas.xml" ) // Ale, como toda la vida.
if hFile = -1
? "Error"
endif
oXml := TXmlDocument():New( hFile )
Asi, que podemos observar como nos hace falta un handle de fichero para poder operar con el fichero XML.
Ok, ahora podemos, averiguar si hubo algún tipo de problema a la hora
de crear el objeto.
if oXml:nStatus != HBXML_STATUS_OK
switch oXml:nStatus
case HBXML_STATUS_ERROR
? "Ay! pillin, nos jorobo alguna cosa....!!"
case HBXML_STATUS_MALFORMED
? "Please, only XML ,po dios!!"
end
return
endif
Bien, ya tenemos el objeto oXml...
¿ Que haremos ahora ... continuará.... ?
******************************************************************************
2.-Leccion. Busca, busca, que algo encontraremos...
******************************************************************************
Bien, nosotros ya aprendimos como crear un objeto que contiene un fichero
XML, o mas bien una referencia a dicho fichero.
Imaginemos por un momento, que tenemos un fichero XML, como este:
<?xml version="1.0" encoding="iso-8859-1"?>
<PETICION id="ACTUALIZA_GRUPO">
<LPARAMS>
<PARAM reserva="1272" agencia="0" central="0" facturar="1230"/>
<PARAM>
<HABITACION tipohab="DB">
<DIA fecha="01/01/2006" habs_act="1" prec_act="15.5" />
<DIA fecha="02/01/2006" habs_act="2" prec_act="125.5" />
<DIA fecha="03/01/2006" habs_act="3" prec_act="1225.5" />
</HABITACION>
<HABITACION tipohab="DUI">
<DIA fecha="01/01/2006" habs_act="4" prec_act="1.5" />
<DIA fecha="02/01/2006" habs_act="5" prec_act="10.5" />
<DIA fecha="03/01/2006" habs_act="6" prec_act="100.5" />
</HABITACION>
</PARAM>
</LPARAMS>
Bien, ahora a nosotros , lo que nos interesa es buscar el nodo inicial en el cual vamos a procesar, para ello, vamos a buscar el
id ="ACTUALIZA_GRUPO".
¿ Como nos traemos un nivel en concreto ?
Podemos usar el method FindFirst() para localizar el "id" que nos interesa:
oNodo := oXml:FindFirst( "PETICION", "id", "ACTUALIZA_GRUPO" )
La búsqueda, devuelve o el objeto oNodo que a encontrado , o en su defecto
devolverá NIL si no ha encotrado nada.
Bien... seguimos...
Estando ya con el nodo , podriamos buscar un nodo igual, si tuvieramos
diferentes <PETICION id="ACTUALIZA_GRUPO">, con oXml:FindNext().
Ahora , podemos recorrer los nodos en busca de valores que nos interesa
coger:
DO WHILE oNode != NIL
// Es un TAG y contiene algo, cogemos los atributos.
IF oNode:cName = "PARAM" .AND.;
oNode:nType == HBXML_TYPE_TAG .AND. ;
oNode:GetAttribute("reserva") != NIL
? "------------------------------------------"
? oNode:GetAttribute("reserva")
? oNode:GetAttribute("agencia")
? oNode:GetAttribute("central")
? oNode:GetAttribute("facturar")
? "------------------------------------------"
ENDIF
Paramos aqui, y vemos lo que hemos estado realizando, el IF que existe,
es que hay 2 tags con PARAM, uno con valores, y otro sin nada.
<PARAM reserva="1272" agencia="0" central="0" facturar="1230"/>
<PARAM>
A nosotros, simplemente nos interesa procesar el que contiene valores,
para ello, con el IF, selecciono el que necesito.
Seguro que hay otro camino mas simple para hacer lo mismo, pero este es el
que he encontrado en el poco tiempo que he podido mirar.
Y podemos ver, una cosa bien simple, y es como obtener los valores de
los atributos que nos interesa, a traves del method ::GetAttribute().
Realmente, los atributos son guardados en una tabla Hash, y es la data
::aAttributes la que contiene dichos datos.
Entonces, simplemente haciendo:
X := oNode:GetAttribute("reserva"), , X valdria "1272".
Ok, ahora hemos visto como podemos seleccionar un TAG y como podemos
obtener los valores de dicho TAG, pero ahora nos interesa recorrer el TAG
de las habitaciones para obtener los valores que nos han pasado.
Para ello, a continuacion del siguiente codigo que antes habiamos puesto.
// Si el nodo es la habitacion
IF oNode:cName = "HABITACION"
? " NºHABITACION :" + oXmlNode:GetAttribute("tipohab")
oNodeDia := oNode:NextInTree()
WHILE oNodeDia != NIl
IF oNodeDia:cName = "DIA"
? oNodeDia:GetAttribute("fecha") + " " +;
oNodeDia:GetAttribute("habs_act") + " " +;
oNodeDia:GetAttribute("prec_act")
ELSE
EXIT // Salimos del nodo
ENDIF
oNodeDia := oNodeDia:NextInTree()
END WHILE
ENDIF
oNode := oNode:NextInTree()
ENDDO
RETURN NIL
// Cerramos el XML
fClose( hFile )
Bueno, del codigo podemos comentar el method NextInTree(), que seria
un simil a las dbfs como el DbSkip(), ( idea muy acertada de Fredy ), y
que va saltando de nodo a nodo, independientemente del nivel de anidacion,
siempre cogera el siguiente nodo, y dicho method , nos devuelve un objeto
si encontro algo o NIL en caso contrario.
Bueno, hemos podido ver como podemos recorrer los nodos y coger valores.
Proxima leccion:
3.- Guardaté, condenado...
Umm.. antes de grabar de XML, acabo de terminar de comprender y poner en funcionamiento el tema de los Iterator, y he escrito esto en un momentin.
******************************************************************************
Proxima leccion: 2.5.- Busca , Mister "Iterator".
******************************************************************************
Bueno, anteriomente, vimos como localizabamos un valor, y nos moviamos por
el documento XML para obtener valores.
Bien, ahora, vamos a buscar de otra manera, a traves de un Iterator, pero
¿ Que es un Iterator ?
Digamos que un Iterator es una especie de localizador en un nodo en concreto.
Cuando creamos un objeto, oXml, este contiene el nodo root, por lo tanto,
podemos buscar en todo el documento haciendo:
oIter := TXMLIteratorScan():New( oXml:oRoot )
oNodo := oIter:Find( "PETICION", "id", "ACTUALIZA_GRUPO" )
Bien, ahora imaginemos que tenemos en el fichero XML , copialo al XML que
definimos anteriormente.
//NODO 1
<PARAM id="detalle1" >
<PARAM linea="DB">
<PARAM valor="este"> </PARAM>
</PARAM>
</PARAM>
//NODO 2
<PARAM id="detalle2" >
<PARAM linea="DBU">
<PARAM valor="AQUI"> </PARAM>
</PARAM>
</PARAM>
//NODO 3
<PARAM id="detalle1" >
<PARAM linea="XXX">
<PARAM valor="LALA"> </PARAM>
</PARAM>
</PARAM>
Ahora veremos como localizar un nodo, y recoger ese nodo.
// Localizamos
oIter := TXMLIteratorScan():New( oXml:oRoot )
oNode := oIter:Find( "PARAM", "id", "detalle1" )
Ahora teniendo este nodo, si hacemos :
? oNode:GetAttribute("id") , vamos a obtener "detalle1".
Asi, para poder coger linea podemos hacer:
oNodeHijo := oNode:oChild
? oNodeHijo:GetAttribute("linea") , vamos a obtener "DB".
Y el siguiente:
oNodeHijo2 := oNodeHijo:oChild
? oNodeHijo2:GetAttribute("valor") , vamos a obtener "este".
Como podeis observar , hay varias maneras de obtener el mismo resultado,
dependiendo tambien como tengais realizado el XML, hareis o deshareis .
Los Iterator tambien puede ir a traves del method Next(), oIter:Next.
Asi, si hacemos oIter:Next(), vamos a obtener el siguiente nodo, teniendo en
cuenta la busqueda anterior :
oNode := oIter:Next() , vamos a obtener , si preguntamos:
? oNode:GetAttribute("id") , vamos a obtener "detalle1".
Vamos a coger el hijo :
oNodeChild := oNode:oChild
? oNodeChild:GetAttribute("linea") , vamos a obtener "XXX".
En definitiva, para usar este tipo de clases, recordad que es como recorrer
un arbol , con sus ramas, flores, etc...
( cuando no chuta pues has tropezado con estiercol, etc... )
Hay varias opciones más, pero esto es un complemento a la ayuda de
Freddy, para que podáis comprender mejor esa documentación.