martes, 24 de noviembre de 2015

Convertir XML a Hash en Harbour ( update II )



En eso que estaba yo pensando en como acceder a un XML que contiene solo nodos de elementos,
sin atributos, sin tener que estar continuamente haciendo búsquedas.

Pues vamos a dotarle a Harbour la manera de recorrer cualquier nodo, y devolver un Hash con
el contenido.

Los subnodos, simplemente serán arrrays con Hash.
He aquí el codigo de ejemplo;

UDPATE. Soporte para atributos.
Ahora, se crea un hash con el  nombre del elemento, mas @attribute  que va a contener un hash con los atributos del nodo, de esta manera para obtener el nombre del nodo, node1;
hHash["node1@attribute"]["nombre"] mostrará pepe

UPDATE II. Fix espacios en blanco
Se detecta que un nodo, <nodo>Ejemplo XML</nodo>
El valor del Hash["nodo"] será igual a Ejemplo, y debería ser Ejemplo XML
Viktor me ha iluminado, comentando que hay que usar OPAQUE

XML

<?xml version="1.0" encoding="utf-8"?>
<data>
    <node1 nombre="pepe">Ejemplo XML</node1>
    <node2>val2</node2>
    <node3>val3</node3>
    <group>
        <node1>val4</node1>
        <node2>val5</node2>
        <node3>val6</node3>
    </group>
    <group>
        <node1>val24</node1>
        <node2>val25</node2>
        <node3>val26</node3>
      <extras>
            <node1>Extra_val24</node1>
            <node2>Extra_val25</node2>
            <node3>Extra_val26</node3>
        </extras>
    </group>
    <node7>val7</node7>
    <node8>val8</node8>
</data>

PRG

FUNCTION Test( file )
  Local pRoot, hHash

  pRoot :=   mxmlLoadString( NIL, hb_MemoRead( file ),  @type_cb() )
  hHash := XMLtoHash( pRoot, "data" )
 
  // Podemos acceder fácilmente ;
  ??  hHash["node1"]  // Muestra Ejemplo XML
  ??  hHash["node2"]  // Muestra val2  ??  hHash["group"][1]["node1"] // Muestra val4
  ??  hHash["group"][2]["extras"][1]["node1"] // Muestra Extra_val24

    ??  hHash["node1@attribute"]["nombre]  // Muestra pepe

     mxmlDelete( pRoot )

 RETURN nil

STATIC FUNCTION type_cb( node ) ;  RETURN MXML_OPAQUE
 


// ---------------------------------------------------------------------------//
FUNCTION XMLtoHash( pRoot, cElement )
   Local pNode, hNext
   Local Map := {=>}

   if empty( cElement )
      pNode := pRoot
   else  
      pNode := mxmlFindElement( pRoot, pRoot, cElement, NIL, NIL, MXML_DESCEND )
   endif
     
   IF Empty( pNode )
      RETURN Map
   ENDIF

   hNext := mxmlWalkNext( pNode, pNode, MXML_DESCEND )
   Map :=  NodeToHash( hNext )

  return Map

// ---------------------------------------------------------------------------//

STATIC FUNCTION NodeToHash( node  )
   Local wt := 0
   Local hNext
   Local hHashChild := {=>}
   Local hHash := {=>}

   WHILE node != NIL
         
         IF mxmlGetType( node ) == MXML_ELEMENT

            if HB_HHASKEY( hHash, mxmlGetElement( node ) )
               if valtype( hHash[ mxmlGetElement( node ) ] ) <> "A"
                  hHash[ mxmlGetElement( node ) ] := mxmlGetOpaque( node )
               else
                 // Es un array, por lo tanto, no lo tocamos
               endif  
            else                  
               hHash[ mxmlGetElement( node ) ] := mxmlGetOpaque( node )
            endif    
            if HB_MXMLGETATTRSCOUNT( node ) > 0
               hHash[ mxmlGetElement( node ) + "@attribute"] := HB_MXMLGETATTRS( node )
            endif  
 

              if empty( mxmlGetOpaque( node ) ) // Miramos dentro
               hNext := mxmlWalkNext( node, node, MXML_DESCEND )  
               if hNext != NIL
                  if empty( hHash[ mxmlGetElement( node ) ]  )
                     hHash[ mxmlGetElement( node ) ] := {}
                  endif  
                   hHashChild :=  NodeToHash( hNext  )
                   if hHashChild != NIL
                      AADD( hHash[ mxmlGetElement( node ) ], hHashChild )
                   endif  
               endif  
            endif
         ENDIF   

         node := mxmlGetNextSibling( node )
                    
   END WHILE

RETURN hHash
 

Aqui muestra pantallazo de obtener de un XML de SEPA de prueba;
 
 

Android y Git. Disponer del hash automáticamente.

Una de las cosas a las que estoy acostumbrado, es tener siempre en mi código, el hash/tag/versión del control de versiones que estoy usan...