XML em AdvPL – Parte 02

Introdução

No post anterior (XML em AdvPL – Parte 01) vimos o que é um XML, e vimos uma das formas de como podemos ler sua estrutura e conteúdo em AdvPL. Nesse post, vamos ver uma ferramenta interessante para avaliar conteúdo de qualquer XML.

Programa U_ZXMLKIT

Em AdvPL podemos criar uma interface de forma relativamente simples, basta conhecer um pouco sobre como a hierarquia dos componentes de interface funcionam, e com poucas linhas de código podemos criar programinhas bem eficientes. Usando um container de folders e alguns botões, junto com a classe tXmlManager, vejamos abaixo uma ferramenta útil para você informar um XML (texto ou arquivo), e ela gerar pra você uma lista de todos os dados e conteúdos disponíveis neste XML. O fonte atualizado já está disponível nesse link, no GITHUB.

#include 'protheus.ch'

/*
Funcao 		U_zXMLKit()
Autor		Julio Wittwer
Data 		14/07/1029
Descrição 	Interface para teste de Parser de XML

		Realiza o parser de uma string ou arquivo XML, e levanta todas as informações dos nodes 
		que podem ser extraídas do XML usando DOM 
*/


User Function zXMLKit()
Local oDlg
Local oGetXML
Local cXML := ''
Local oXml                    
Local cXmlFile := space(80)              
Local nOpList
Local aNodeList := {''}
Local oListResult 

oXml := tXmlManager():New()

// Cria uma caixa de diálogo com área util de 800x600  PIXELs
DEFINE DIALOG oDlg TITLE "AdvPL XML Kit" FROM 0,0 TO 600,800 PIXEL

@ 0,0 FOLDER oFolders PROMPTS "XML String","XML File","Resultado"  OF oDlg  PIXEL
oFolders:ALIGN := CONTROL_ALIGN_ALLCLIENT

// Folder 1 . Parser da String informada

@ 10,10 SAY "Informe a String XML para Parser" OF oFolders:aDialogs[1] PIXEL
@ 20,10 GET oGetXML VAR cXML MULTILINE SIZE 380, 200 of oFolders:aDialogs[1]  PIXEL

@ 230,10 BUTTON oBtmParse PROMPT "&Parser" ;
  ACTION (DoPArser(oDlg,oListResult,oXml,cXml)) ;
  SIZE 040, 013 OF oFolders:aDialogs[1]  PIXEL

@ 230,350 BUTTON oBtmParse PROMPT "Sai&r" ;
  ACTION (oDlg:End()) ;
  SIZE 040, 013 OF oFolders:aDialogs[1]  PIXEL


// Folder 2 . Parser do Arquivo XML informado

@ 10,10 SAY "Informe o arquivo XML para Parser" OF oFolders:aDialogs[2] PIXEL
@ 20,10 GET oGetFile VAR cXmlFile SIZE 380, 13 of oFolders:aDialogs[2]  PIXEL

@ 230,10 BUTTON oBtnParseF PROMPT "&Parser" ;
  ACTION (DoParserFile(oDlg,oListResult,oXml,cXmlFile)) ;
  SIZE 040, 013 of oFolders:aDialogs[2]  PIXEL

@ 230,350 BUTTON oBtmParse PROMPT "Sai&r" ;
  ACTION (oDlg:End()) ;
  SIZE 040, 013 of oFolders:aDialogs[2]  PIXEL

// Folder 3 - ListBox com o resultado 

@0,0 LISTBOX oListResult VAR nOpList;
	ITEMS aNodeList ;
	OF oFolders:aDialogs[3]  PIXEL

oListResult:ALIGN := CONTROL_ALIGN_ALLCLIENT

ACTIVATE DIALOG oDlg CENTER 

Return

// Realiza o parser do XML 
STATIC Function DoPArser(oDlg,oListResult,oXml,cXml)
Local lOk
Local nTimer 
Local cMsg

If Empty(cXml)
	MsgInfo('Infome um XML para executar o Parser')
	Return
Endif

// remove quebras de linha e tabulações
cXml := Strtran(cXml,chr(13),'')
cXml := Strtran(cXml,chr(10),'')
cXml := Strtran(cXml,chr(9),'')

nTimer := seconds()
lOk := oXml:Parse(cXml)	
nTimer := seconds()-nTimer

If !lOk
	cMsg := oXml:Error()
	oListResult:SetArray({' '})
	MsgStop(cMsg,"Parse Error")     
Else
	cMsg := "Parser executado em "+str(nTimer,12,2)+' s.'
	BuildResult(oXml,oListResult)
	MsgInfo(cMsg,"Parse OK")     
Endif
	
Return

// Faz parser de um XML lido do disco a partir do RootPAth

STATIC Function DoParserFile(oDlg,oListResult,oXml,cXmlFile)
Local lOk
Local nTimer 
Local cMsg
                                       
cXmlFile := alltrim(cXmlFile)

If Empty(cXmlFile)
	MsgStop('Infome um arquivo XML para executar o Parser')
	Return
Endif

If !file(cXmlFile)
	MsgStop('Arquivo ['+cXmlFile+'] não encontrado.')
	Return
Endif

nTimer := seconds()
lOk := oXml:ParseFile(cXmlFile)	
nTimer := seconds()-nTimer

If !lOk
	cMsg := oXml:Error()
	oListResult:SetArray({' '})
	MsgStop(cMsg,"Parse Error")     
Else
	cMsg := "Parser executado em "+str(nTimer,12,2)+' s.'
	BuildResult(oXml,oListResult)
	MsgInfo(cMsg,"Parse OK")     
Endif
	
Return
                                 
// Monta os dados para um ListBox com os resultados obtidos 

STATIC Function BuildResult(oXml,oListResult,aResult,nStack)
Local cRow          

DEFAULT aResult := {}
DEFAULT nStack := 1
 
While .T.

	// Se o nó atual tem um dado, armazena
	If !XMLempty(oXML:cText)
		cRow := 'Path ['+oXML:cPath+'] Value ['+oXML:cText+']'
		aadd(aResult,cRow)
	Endif
	
	IF oXml:DOMHASATT()
		// Se o nó atual tem atrubitos, lê todos
		aAtt := oXml:DOMGETATTARRAY()
		For nI := 1 to len(aAtt)
			cRow := 'Path ['+oXML:cPath+'] Att ['+aAtt[nI][1]+'] Value ['+aAtt[nI][2]+']'
			aadd(aResult,cRow)
		Next
	Endif
	
	If oXml:DOMHasChildNode()
		// Se o nó atual tem um filho, entra nele para avaliar o conteudo
		oxml:DOMChildNode()
		BuildResult(oXml,oListResult,aResult,nStack+1)
		oxml:DOMParentNode()
	Endif
	
	IF oXml:DOMHasNextNode()
		// Se existe um próximo nó neste nivel, 
		// vai pra ele e continua analizando 
		oxml:DOMNextNode()
		LOOP
	Endif

	// Este nó já foi analizado inteiro, sai do loop 
	EXIT
	
Enddo

If nStack == 1
	// Seta o array de resultado para o ListBox
	If len(aResult) > 0
		oListResult:SetArray(aResult)
	Else
		oListResult:SetArray({' *** NO RESULT *** '})
	Endif
Endif

Return

// Remove caracteres irrelevantes do valor de um node 
// para ver se ele é realmente vazio

Static Function XMLempty(cNodeValue)

cNodeValue := strtran(cNodeValue,chr(13),'')
cNodeValue := strtran(cNodeValue,chr(10),'')
cNodeValue := strtran(cNodeValue,chr(9),'')
cNodeValue := strtran(cNodeValue,' ','')

Return empty(cNodeValue)

Agora, vamos ver o resultado prático deste fonte. Vou usar como exemplo o XML de “pessoas” do post anterior.

<pessoas>
<pessoa tipo="F">
<nome>Julio</nome>
<cpf>123.456.789/09</cpf>
</pessoa>
<pessoa tipo="J">
<nome>Empresa 123 SA</nome>
<cnpj>12.345.678-0001/99</cnpj>
</pessoa>
</pessoas>

Ao executar o programa, entramos com o XML no campo de texto.

zXmkKit 1

Após pressionar o botão “Parser”, o XML será avaliado e os resultados dos dados identificados estão no Folder “Resultado”. Caso você queira informar um arquivo, já para o Folder “XML File”, entre com o caminho do arquivo a partir do RootPath do ambiente atual e pressione o botão “Parser”. Segue o resultado obtido no folder “Resultado”:

zXmkKit 2

Reparem que para cada node do XML avaliado, é mostrado o caminho até o node/dado (formato XPATH) e o valor correspondente, e no caso de atributos, é informado o caminho XPATH, seguido do atributo e do valor do tributo encontrado.

Avaliando o fonte

A função BuildResult() varre recursivamente a estrutura do XML usando os métodos DOM da classe tXmlManager, levantando todas as informações de todos os nós de forma sequencial, e armazenando o resultado em um array de strings, que é atribuído a um tListBox no último Folder. Tão simples quanto isso.

O importante é que a forma de avaliação verifica se o nó atual tem nodes “filho”, e entra neles para fazer a varredura, retornando ao node anterior após a análise, e verificando se existe próximo node no mesmo nível, assim todos os nós do XML são percorridos.

Conclusão

Ainda existem mais truques e recursos para estes componentes, que serão abordados nos próximos posts desta sequência.

Novamente agradeço a audiência, e desejo a todos TERABYTES DE SUCESSO !!!

 

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s