how XML extract tags, keys
Posted: Tue Dec 21, 2021 11:19 am
I have XML files, see structure but how (easy) extract some tags, keys or other data
examle data in attcahment
examle data in attcahment
Exclusive forum for HMG, a Free / Open Source xBase WIN32/64 Bits / GUI Development System
http://mail.hmgforum.com/
Take a look at this sample, it might be useful to you.dragancesu wrote: ↑Tue Dec 21, 2021 11:19 am I have XML files, see structure but how (easy) extract some tags, keys or other data
examle data in attcahment
It seems to me that a better solution will be to directly process the xml file and save the read values directly to the target databases. Here is an example:dragancesu wrote: ↑Wed Dec 22, 2021 8:22 am @edk Thank you, nice
this is for eInvoice goverment project, xml download from site and task is load into invoice data structure
Is posible something like in attachment
1. step xml_vadi
2. step xml_fnc
and result is DBF with data from XML
Code: Select all
#require "hbmxml.hbc"
#include "hmg.ch"
#define cTab Chr ( 9 )
Function Main
Local cXmlString, cXmlFile, aXML, i, cPath, cKey, xValue, hAttrib, lShowMessage
//To Make DO CASE ... sequence only
Local aPath, Attrib_Key
cXmlFile := GetFile( {{'XML File', '*.xml'}}, 'Select XML', hb_CWD(), .F. , .T. )
IF EMPTY( cXmlFile )
RETURN Nil
ENDIF
lShowMessage := MsgYesNo( "Show processed data?" )
cXmlString := FileStr( cXmlFile )
aXML := hb_XmlToArray( cXmlString, .T. /* .F. = encode root node also */ )
//To Make DO CASE ... sequence only
aPath :={}
STRFILE ("****** DO CASE for the " + hb_FNameNameExt ( cXmlFile ) + " file structure ********" + CRLF + "DO CASE" + CRLF, "Do_case.prg", .F. )
//
FOR i := 1 TO Len ( aXML )
cPath := aXML [ i ] [ 1 ]
cKey := aXML [ i ] [ 2 ]
xValue := aXML [ i ] [ 3 ]
hAttrib := aXML [ i ] [ 4 ]
// <---- here is xml data processing, e.g. writing to databases according to tags and possibly attributes
IF lShowMessage
/* In this example, I'm displaying the read values, the key and the path to the key along with any attributes */
MsgInfo ('Key Path: "' + cPath + '"' + CRLF + CRLF + ;
'Key: "' + cKey + '"' + CRLF + CRLF + ;
"Value: " + Left ( xValue, 100 ) /* first 100 chars for test only */ + CRLF + CRLF +;
IF( Len( hAttrib ) > 0 , "Attribute(s): " + hb_JsonEncode ( hAttrib ), "" ), "Processing data from an XML file." )
ENDIF
/* Below I used the read XML file structure to generate a DO CASE ... sequence that I can use to process data from the XML file, e.g. to write values to the appropriate database fields. */
//Make DO CASE ... sequence
IF hb_AScan ( aPath, cPath + "/" + cKey, , , .T. ) = 0
AAdd( aPath, cPath + "/" + cKey)
STRFILE ( cTab + 'CASE cKey == "' + cKey + '" .AND. cPath == "' + cPath + '"' + CRLF + cTab + cTab + CRLF , "Do_case.prg", .T. )
IF LEN( hAttrib ) > 0
FOR EACH Attrib_Key IN hb_HKeys ( hAttrib )
STRFILE ( cTab + cTab + 'IF hb_HHasKey( hAttrib, "' + Attrib_Key + '" )' + CRLF + ;
cTab + cTab + cTab + 'xAttribValue := hb_HGet ( hAttrib, "' + Attrib_Key + '" )' + CRLF + CRLF + ;
cTab + cTab + 'ENDIF' + CRLF + CRLF , "Do_case.prg", .T. )
NEXT
ENDIF
ENDIF
//End Make DO CASE ... sequence
NEXT i
STRFILE ("END CASE", "Do_case.prg", .T. )
MsgInfo ('The file "Do_case.prg" has been created with the DO CASE sequence for the "' + hb_FNameNameExt ( cXmlFile ) + '" file structure.' )
Return
*****************************************************************************
STATIC FUNCTION hb_XMLtoArray( cXML, lOmitHeader )
LOCAL pXML
Private __aXML := {}
hb_default( @lOmitHeader, .T. )
pXML := mxmlLoadString( NIL, hb_utf8ToStr( cXML ), MXML_OPAQUE_CALLBACK )
IF !Empty( pXML )
IF lOmitHeader
hb_ArrayXML_getnodes( mxmlGetFirstChild( pXML ))
ELSE
hb_ArrayXML_getnodes( pXML )
ENDIF
ENDIF
mxmlDelete( pXML )
RETURN __aXML
*****************************************************************************
STATIC FUNCTION hb_ArrayXML_getnodes( pNode )
LOCAL cKey, pChild, xResult, xValue, cPrevKey, hAttrib, pParrentNode, cPath
DO WHILE !Empty( pNode )
IF mxmlGetType( pNode ) == MXML_ELEMENT
cKey := mxmlGetElement( pNode )
pChild := mxmlGetFirstChild( pNode )
xValue := hb_ArrayXML_getnodes( pChild )
IF xValue == NIL
xValue := mxmlGetOpaque( pNode )
IF !( xValue == CRLF )
cPath := ""
pParrentNode := mxmlGetParent( pNode )
DO WHILE !EMPTY ( pParrentNode )
cPrevkey := mxmlGetElement( pParrentNode )
IF Left ( cPrevkey, 4 ) <> "?xml"
cPath := cPrevkey + IF( !Empty( cPath ), "/", "") + cPath
ENDIF
pParrentNode := mxmlGetParent( pParrentNode )
ENDDO
hAttrib := hb_mxmlGetAttrs( pNode )
AADD( __aXML, { cPath, cKey, xValue, hAttrib } )
xResult := __aXML
ENDIF
ENDIF
ENDIF
pNode := mxmlGetNextSibling( pNode )
ENDDO
RETURN xResult
Code: Select all
#require "hbmxml.hbc"
#include "hmg.ch"
#define cTab Chr ( 9 )
Function Main
Local cXmlString, cXmlFile, aXML, i, cPath, cKey, xValue, hAttrib, lShowMessage := .T.
//To Make DO CASE ... sequence only
Local aPath, Attrib_Key
cXmlFile := GetFile( {{'XML File', '*.xml'}}, 'Select XML', hb_CWD(), .F. , .T. )
IF EMPTY( cXmlFile )
RETURN Nil
ENDIF
lShowMessage := MsgYesNo( "Show processed data?" )
cXmlString := FileStr( cXmlFile )
aXML := hb_XmlToArray( cXmlString, .T. /* .F. = encode root node also */ )
//To Make DO CASE ... sequence only
aPath :={}
STRFILE ("****** DO CASE for the " + hb_FNameNameExt ( cXmlFile ) + " file structure ********" + CRLF + "DO CASE" + CRLF, "Do_case.prg", .F. )
//
FOR i := 1 TO Len ( aXML )
cPath := aXML [ i ] [ 1 ]
cKey := aXML [ i ] [ 2 ]
xValue := aXML [ i ] [ 3 ]
hAttrib := aXML [ i ] [ 4 ]
// <---- here is xml data processing, e.g. writing to databases according to tags and possibly attributes
IF lShowMessage
/* In this example, I'm displaying the read values, the key and the path to the key along with any attributes */
MsgInfo ('Key Path: "' + cPath + '"' + CRLF + CRLF + ;
'Key: "' + cKey + '"' + CRLF + CRLF + ;
"Value: " + Left ( xValue, 100 ) /* first 100 chars for test only */ + CRLF + CRLF +;
IF( Len( hAttrib ) > 0 , "Attribute(s): " + hb_JsonEncode ( hAttrib ), "" ), "Processing data from an XML file." )
ENDIF
/* Below I used the read XML file structure to generate a DO CASE ... sequence that I can use to process data from the XML file, e.g. to write values to the appropriate database fields. */
//Make DO CASE ... sequence
IF hb_AScan ( aPath, cPath + "/" + cKey, , , .T. ) = 0
AAdd( aPath, cPath + "/" + cKey)
STRFILE ( cTab + 'CASE cKey == "' + cKey + '" .AND. cPath == "' + cPath + '"' + CRLF + cTab + cTab + CRLF , "Do_case.prg", .T. )
IF LEN( hAttrib ) > 0
FOR EACH Attrib_Key IN hb_HKeys ( hAttrib )
STRFILE ( cTab + cTab + 'IF hb_HHasKey( hAttrib, "' + Attrib_Key + '" )' + CRLF + ;
cTab + cTab + cTab + 'xAttribValue := hb_HGet ( hAttrib, "' + Attrib_Key + '" )' + CRLF + CRLF + ;
cTab + cTab + 'ENDIF' + CRLF + CRLF , "Do_case.prg", .T. )
NEXT
ENDIF
ENDIF
//End Make DO CASE ... sequence
NEXT i
STRFILE ("END CASE", "Do_case.prg", .T. )
MsgInfo ('The file "Do_case.prg" has been created with the DO CASE sequence for the "' + hb_FNameNameExt ( cXmlFile ) + '" file structure.' )
Return
*****************************************************************************
STATIC FUNCTION hb_XMLtoArray( cXML, lOmitHeader )
LOCAL pXML
Private __aXML := {}
hb_default( @lOmitHeader, .T. )
pXML := mxmlLoadString( NIL, hb_utf8ToStr( cXML ), MXML_OPAQUE_CALLBACK )
IF !Empty( pXML )
IF lOmitHeader
hb_ArrayXML_getnodes( mxmlGetFirstChild( pXML ))
ELSE
hb_ArrayXML_getnodes( pXML )
ENDIF
ENDIF
mxmlDelete( pXML )
RETURN __aXML
*****************************************************************************
STATIC FUNCTION hb_ArrayXML_getnodes( pNode )
LOCAL cKey, pChild, xResult, xValue, cPrevKey, hAttrib, pParrentNode, cPath
DO WHILE !Empty( pNode )
IF mxmlGetType( pNode ) == MXML_ELEMENT
cKey := mxmlGetElement( pNode )
pChild := mxmlGetFirstChild( pNode )
xValue := hb_ArrayXML_getnodes( pChild )
IF xValue == NIL
xValue := mxmlGetOpaque( pNode )
IF !(xValue == CRLF)
cPath := ""
pParrentNode := mxmlGetParent( pNode )
IF Left ( cKey, 8 ) == "![CDATA["
xValue := hb_StrShrink ( mxmlGetCDATA( pNode ), 2 )
cKey := mxmlGetElement( mxmlGetParent( pNode ) )
pParrentNode := mxmlGetParent( mxmlGetParent( pNode ) )
ENDIF
DO WHILE !EMPTY ( pParrentNode )
cPrevkey := mxmlGetElement( pParrentNode )
IF Left ( cPrevkey, 4 ) <> "?xml"
cPath := cPrevkey + IF( !Empty( cPath ), "/", "") + cPath
ENDIF
pParrentNode := mxmlGetParent( pParrentNode )
ENDDO
hAttrib := hb_mxmlGetAttrs( pNode )
AADD( __aXML, { cPath, cKey, xValue, hAttrib } )
xResult := __aXML
ENDIF
ENDIF
ENDIF
pNode := mxmlGetNextSibling( pNode )
ENDDO
RETURN xResult