Funcion XMLToHash

Funcion XMLToHash

Postby csincuir » Tue Dec 17, 2024 5:35 pm

Hola, estaba buscando la forma de pasar un XML a Hash, y encontré una función de Rafa Carmona, tomado ideas de esa función, escribí esta otra función, utilizando TXmlDocument:

Code: Select all  Expand view  RUN
FUNCTION XMLToHash(cXMLFile)
   LOCAL oXmlDoc, oRoot, hResult
   
   TRY
      oXmlDoc := TXmlDocument():New(cXMLFile)
   CATCH
      ? "Error: No se puede leer el archivo XML: " + cXMLFile
      RETURN NIL
   END
   
   oRoot := oXmlDoc:oRoot
   hResult := hash()
   
   IF oRoot:oChild == NIL
      RETURN hResult
   ENDIF
   
   ProcessNode(oRoot:oChild, @hResult)
   
   RETURN hResult
   
STATIC FUNCTION ProcessNode(oNode, hContainer)
   LOCAL hNodeData
   
   DO WHILE oNode != NIL
      hNodeData := hash()
     
      // Procesar el contenido del nodo
      IF !Empty(oNode:cData)
         hb_HSet(hNodeData, "value", oNode:cData)
      ENDIF
     
      // Procesar atributos si existen
      IF !Empty(oNode:aAttributes)
         hb_HSet(hNodeData, "attributes", hash())
         HEval(oNode:aAttributes, {|k,v| hb_HSet(hNodeData["attributes"], k, v)})
      ENDIF
     
      // Procesar nodos hijos recursivamente
      IF oNode:oChild != NIL
         ProcessNode(oNode:oChild, @hNodeData)
      ENDIF
     
      // Agregar el nodo al contenedor
      IF hb_HHasKey(hContainer, oNode:cName)
         // Si ya existe una entrada para este nombre de nodo, convertir a array
         IF !HB_ISARRAY(hContainer[oNode:cName])
            hContainer[oNode:cName] := {hContainer[oNode:cName]}
         ENDIF
         AAdd(hContainer[oNode:cName], hNodeData)
      ELSE
         hb_HSet(hContainer, oNode:cName, hNodeData)
      ENDIF
     
      oNode := oNode:oNext
   ENDDO
   
RETURN NIL


Esta funcion coloca:
["value"] para el contenido del nodo
["attributes"] para los atributos del nodo

En la prueba me funciono bien, utilizando el XML que coloco Rafa en su funcion:
Code: Select all  Expand view  RUN
FUNCTION TestXml2Hash()
  Local pRoot, hHash, cXml

  TEXT INTO cXml
    <?xml version="1.0" encoding="utf-8"?>
    <data>
        <node1 nombre="cliente">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>
  ENDTEXT  
 
 
  hHash := XMLtoHash( cXml )
 
    ? hHash["data"]["node1"]["value"]  // Muestra Ejemplo XML  
    ? hHash["data"]["node1"]["attributes"]["nombre"]  // Esto debería mostrar "cliente"

    ?  hHash["data"]["node2"]["value"]  // Muestra val2  
 
   // Para el primer grupo
    ? hHash["data"]["group"][1]["node2"]["value"]     // Mostrará "val5"

    // Para el segundo grupo
    ? hHash["data"]["group"][2]["node2"]["value"]     // Mostrará "val25"

    // Para acceder a extras del segundo grupo
    ? hHash["data"]["group"][2]["extras"]["node1"]["value"]  // Mostrará "Extra_val24"

    xBrowse(hHash)


 RETURN nil
 


Si alguien puede probarla con un XML mas completo, me avisa si funcionó correctamente, o le pueden agregar algo mas de funcionalidad a esta función, lo puedan colocar en el foro.

Saludos

Carlos.
csincuir
 
Posts: 411
Joined: Sat Feb 03, 2007 6:36 am
Location: Guatemala

Re: Funcion XMLToHash

Postby paquitohm » Tue Dec 17, 2024 6:06 pm

Carlos,

Muy interesante tu mejora.
Creo que en el pasado tuve problemas con los atributos y lo solucione con palicos y cañicas.
La única pega que le pongo, según creo, es el uso obligatorio que se ha de hacer del dato "value".
En mi caso esto rompería codigo porque en la funcion que ya uso es suficiente con poner

Code: Select all  Expand view  RUN
?  hHash["data"]["node2"]  // Muestra val2


y según ha quedado tu funcion se necesita poner:
Code: Select all  Expand view  RUN
?  hHash["data"]["node2"]["value"]  // Muestra val2


Salu2

BTW. En mi caso, mi funcion xml2hash() está construidad desde el siguiente codigo de Vikthor: https://github.com/vszakats/hb/blob/mai ... tohash.prg
paquitohm
 
Posts: 281
Joined: Fri Jan 14, 2022 8:37 am

Re: Funcion XMLToHash

Postby csincuir » Tue Dec 17, 2024 6:40 pm

Hola Paco,
Por el tema de los atributos, use la opcion de colocar "value"
Le hice un cambio a la funcion ProcessNode() para que en los nodos que no tienen atributos, se pueda obtener el valor sin tener que colocar la palabra "value":
Code: Select all  Expand view  RUN
? hHash["data"]["node2"]  // Muestra val2 directamente

Pero, en los nodos con atributos, si habría que colocarle esa palabra "value":
Code: Select all  Expand view  RUN
? hHash["data"]["node1"]["value"]  // Para nodos con atributos, aún necesitas "value"
? hHash["data"]["node1"]["attributes"]["nombre"]  // Los atributos siguen igual

Esta es la funcion con este cambio:
Code: Select all  Expand view  RUN
STATIC FUNCTION ProcessNode(oNode, hContainer)
   LOCAL hNodeData, xNodeValue
   
   DO WHILE oNode != NIL
      // Inicializar el valor del nodo
      xNodeValue := IF(!Empty(oNode:cData), oNode:cData, "")
     
      // Decidir si necesitamos una estructura hash o solo el valor
      IF !Empty(oNode:aAttributes) .OR. oNode:oChild != NIL
         // Si hay atributos o hijos, crear estructura hash
         hNodeData := hash()
         hb_HSet(hNodeData, "value", xNodeValue)
         
         // Procesar atributos si existen
         IF !Empty(oNode:aAttributes)
            hb_HSet(hNodeData, "attributes", hash())
            HEval(oNode:aAttributes, {|k,v| hb_HSet(hNodeData["attributes"], k, v)})
         ENDIF
         
         // Procesar nodos hijos recursivamente
         IF oNode:oChild != NIL
            ProcessNode(oNode:oChild, @hNodeData)
         ENDIF
         
         xNodeValue := hNodeData
      ENDIF
     
      // Agregar el nodo al contenedor
      IF hb_HHasKey(hContainer, oNode:cName)
         // Si ya existe una entrada para este nombre de nodo, convertir a array
         IF !HB_ISARRAY(hContainer[oNode:cName])
            hContainer[oNode:cName] := {hContainer[oNode:cName]}
         ENDIF
         AAdd(hContainer[oNode:cName], xNodeValue)
      ELSE
         hb_HSet(hContainer, oNode:cName, xNodeValue)
      ENDIF
     
      oNode := oNode:oNext
   ENDDO
   
   RETURN NIL


Las pruebas quedarían de esta forma:
Code: Select all  Expand view  RUN
? hHash["data"]["node1"]["value"]  // Muestra Ejemplo XML  
? hHash["data"]["node1"]["attributes"]["nombre"]  // Esto debería mostrar "pepe"
?  hHash["data"]["node2"] //["value"]  // Muestra val2  
// Para el primer grupo
? hHash["data"]["group"][1]["node2"] //["value"]     // Mostrará "val5"

// Para el segundo grupo
? hHash["data"]["group"][2]["node2"] //["value"]     // Mostrará "val25"

// Para acceder a extras del segundo grupo
? hHash["data"]["group"][2]["extras"]["node1"] //["value"]  // Mostrará "Extra_val24"

A ver si te funciona con este cambio.

Saludos.

Carlos.
csincuir
 
Posts: 411
Joined: Sat Feb 03, 2007 6:36 am
Location: Guatemala


Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: Google [Bot] and 13 guests