Exemplo de manipulação de XML via AdvPL

Olá pessoal…

Hoje vou mostrar dois exemplos em AdvPL para manipular arquivos XML, um gerando o arquivo e outro lendo o conteúdo.

Para gerar o arquivo em XML, você pode utilizar as funções padrão para criação de arquivos (como MemoWrite, Fwrite e FWFileWriter), escrevendo as tags e seu conteúdo, por exemplo:

fWrite(nHdl, "<data>"+dToC(dDataBase)+"</data>"+Chr(13)+Chr(10))

Agora, para ler, é necessário usar a função XMLParser, ou a XMLParserFile, a diferença, é que a primeira converte uma string da memória, e a segunda, um arquivo estando dentro da Protheus Data. Em ambas, são passados 3 parâmetros, qual será a substituição logo no primeiro caracter dos nós (geralmente utilizado o underscore / underline), em seguida por referência uma variável que identifica os erros e outra os avisos. Por exemplo:

oLido := XmlParser(MemoRead(cDirect+cArquivo), cReplace, @cErros, @cAvisos)

No exemplo desenvolvido, a estrutura do XML é bem simples, tendo apenas uma lista de produtos.

Arquivo XML gerado

Arquivo XML gerado

Debugando o fonte, podemos ver a estrutura do nosso objeto.

Objeto gerado pelo XMLParser

Objeto gerado pelo XMLParser

E ao finalizar, é mostrado uma mensagem dos produtos encontrados no objeto.

Mensagem de encerramento da Rotina

Mensagem de encerramento da Rotina

Abaixo o exemplo desenvolvido.

//Bibliotecas
#Include "Protheus.ch"
#Include "TopConn.ch"

/*/{Protheus.doc} zTstXML
Função que testa a Criação e Leitura de um arquivo XML
@type function
@author Atilio
@since 17/08/2016
@version 1.0
	@example
	u_zTstXML()
/*/

User Function zTstXML()
	Local aArea      := GetArea()
	Private cDirect  := GetTempPath()
	Private cArquivo := "teste.xml"
	
	//Criação do arquivo XML
	fCriaXML()
	
	//Leitura do arquivo XML
	fLeXML()
	
	RestArea(aArea)
Return

/*---------------------------------------------------------------------*
 | Func:  fCriaXML                                                     |
 | Autor: Daniel Atilio                                                |
 | Data:  17/08/2016                                                   |
 | Desc:  Função que cria o arquivo XML                                |
 *---------------------------------------------------------------------*/
		
Static Function fCriaXML()
	Local nHdl  := 0
	Local aArea := GetArea()
	Local cQry  := ""
	Local nAtu  := 1
	
	//Cria o arquivo
	nHdl := fCreate(cDirect+cArquivo)
		
	//Se houve erro na criação
	If nHdl == -1
		MsgStop("Não foi possível gerar o arquivo!")
	
	Else
		//Monta a query de produtos (pega até o recno 50)
		cQry := " SELECT "
		cQry += "    B1_COD, B1_DESC "
		cQry += " FROM "
		cQry += "    "+RetSQLName('SB1')+" SB1 "
		cQry += " WHERE "
		cQry += "    B1_FILIAL = '"+FWxFilial('SB1')+"' "
		cQry += "    AND R_E_C_N_O_ <= 50 "
		cQry += "    AND SB1.D_E_L_E_T_ = ' ' "
		TCQuery cQry New Alias "QRY_SB1"
		
		//Monta o XML
		fWrite(nHdl, "<?xml version='1.0' encoding='UTF-8' ?>"+Chr(13)+Chr(10))
		fWrite(nHdl, "<dados>"+Chr(13)+Chr(10))
		fWrite(nHdl, "<data>"+dToC(dDataBase)+"</data>"+Chr(13)+Chr(10))
		fWrite(nHdl, "<hora>"+Time()+"</hora>"+Chr(13)+Chr(10))
		fWrite(nHdl, "<produtos>"+Chr(13)+Chr(10))
		While !QRY_SB1->(EoF())
			fWrite(nHdl, '  <produto id="'+cValToChar(nAtu)+'">'+Chr(13)+Chr(10))
			fWrite(nHdl, "    <codigo>"+QRY_SB1->B1_COD+"</codigo>"+Chr(13)+Chr(10))
			fWrite(nHdl, "	<descricao>"+QRY_SB1->B1_DESC+"</descricao>"+Chr(13)+Chr(10))
			fWrite(nHdl, "  </produto>"+Chr(13)+Chr(10))
			
			nAtu++
			QRY_SB1->(DbSkip())
		EndDo
		QRY_SB1->(DbCloseArea())
		fWrite(nHdl, "</produtos>"+Chr(13)+Chr(10))
		fWrite(nHdl, "</dados>"+Chr(13)+Chr(10))
		
		//Finalizando o Handle
		fClose(nHdl)
		
		//Abrindo o arquivo
		ShellExecute("OPEN", cArquivo, "", cDirect, 0 )
	EndIf
	
	RestArea(aArea)
Return

/*---------------------------------------------------------------------*
 | Func:  fLeXML                                                       |
 | Autor: Daniel Atilio                                                |
 | Data:  17/08/2016                                                   |
 | Desc:  Função que faz a leitura do arquivo XML                      |
 *---------------------------------------------------------------------*/
		
Static Function fLeXML()
	Local oLido    := Nil
	Local oProds   := Nil
	Local nAtual   := 0
	Local cReplace := "_"
	Local cErros   := ""
	Local cAvisos  := ""
	Local cMsg     := ""
	
	//Se o arquivo existir
	If File(cDirect+cArquivo)
		//Lendo o arquivo com XMLParser (lê a string), caso queira ler o arquivo direto, utilize o XMLParserFile (o arquivo deve estar dentro da system)
		oLido := XmlParser(MemoRead(cDirect+cArquivo), cReplace, @cErros, @cAvisos)
		
		//Se tiver erros, mostra ao usuário
		If !Empty(cErros)
			Aviso('Atenção', "Erros: "+cErros, {'Ok'}, 03)
		EndIf
		
		//Se tiver avisos, mostra ao usuário
		If !Empty(cAvisos)
			Aviso('Atenção', "Avisos: "+cAvisos, {'Ok'}, 03)
		EndIf
		
		//Montando a Mensagem, data e hora
		cMsg := "Data: "+oLido:_Dados:_Data:Text + Chr(13)+Chr(10)
		cMsg := "Hora: "+oLido:_Dados:_Hora:Text + Chr(13)+Chr(10)
		
		//Percorrendo os produtos
		oProds := oLido:_Dados:_Produtos:_Produto
		For nAtual := 1 To Len(oProds)
			cMsg += "ID: "+oProds[nAtual]:_ID:Text+", "
			cMsg += "Código: "+oProds[nAtual]:_Codigo:Text+", "
			cMsg += "Descrição: "+oProds[nAtual]:_Descricao:Text
			cMsg += Chr(13)+Chr(10)
		Next
		
		//Mostrando a mensagem do xml lido
		Aviso('Atenção', cMsg, {'Ok'}, 03)
	EndIf
Return

Bom pessoal, por hoje é só.
Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

16 Responses

  1. Caio de Paula disse:

    Dan_Atilio, tive um XML que ja está no banco de dados com mais de 65565 bytes, e o memoread nao leu inteiro… e o parser deu erro por conta disso, voce tem ideia de como ler esse XML que ta no banco de dados?

  2. Súlivan Simões disse:

    Eu passei por este mesmo problema do Caio.
    Quando alterei a chave MaxStringSize o sistema começou a ler o arquivo. Porém ele lia o arquivo e cortava seu conteúdo quando atingia o limite de 65565 bytes

    Caso mais alguém passe por isso. O problema foi solucionado por definitivo criando a função a baixo e chamando-a no lugar da memoread

    Segue função.

    #Include “protheus.ch”

    /*/{Protheus.doc} fReadArq
    Summary : Efetua leitura de um arquivo XML localizado em determinado diretório
    @Author : Súlivan Simões Silva
    @Since : 06/06/2019
    @version : 1.0
    @Param : cArquivo, Caracter, Nome do arquivo, deve-se informar o path (diretorio) completo
    @Return : cArqLido, Caracter, conteudo lido do arquivo XML
    @Link : http://tdn.totvs.com/display/framework/FWFileReader
    Obs : Função criada devido a MemoRead() cortar a informação de arquivos superiores a 65kb
    Esse corte era efetuado mesmo quando a chave MaxStringSize do Appserver era aumentada.
    @exemple :
    fReadArq(“c:\dir\nomedoarquivo.xml”)
    /*/
    user function fReadArq(cArquivo)

    local aArea := getArea()
    local cArqLido := ”
    local oFile := FWFileReader():New(cArquivo)

    if ( oFile:Open() )

    while( oFile:hasLine() )

    cArqLido += oFile:GetLine()
    enddo

    oFile:Close()
    endif

    restArea( aArea )
    return cArqLido

  3. Artur Nucci Ferrari disse:

    Dan_Atilio, boa tarde.

    Muito útil essas funções, mas abrindo o xml gerado pelo pelo Notepad, o padrão é o ANSI.

    De que forma podemos gerar o arquivo em UTF-8?

    Abraços.
    Artur

  4. Davi Miranda disse:

    Boa tarde,
    Como vai?

    Eu gostaria que ele lesse o XML inteiro tem como fazer isso? Eu usei somente o Le XML mas ele não e o xml todo….

    Criei uma função que eu seleciono um XML e queria que lesse todo ele como chave e etc…

    Static Function fLeXML()
    Local oLido := Nil
    Local oProds := Nil
    Local nAtual := 0
    Local cReplace := “_”
    Local cErros := “”
    Local cAvisos := “”
    Local cMsg := “”
    //Local oVerde := LoadBitmap(GetResources(),’BR_VERDE’)
    //Local oAmarelo := LoadBitmap(GetResources(),’BR_AMARELO’)
    //Local oVermelho := LoadBitmap(GetResources(),’BR_VERMELHO’)
    //Local oPreto := LoadBitmap(GetResources(),’BR_PRETO’)
    Local oAzul := LoadBitmap(GetResources(),’BR_AZUL’)

    //Se o arquivo existir
    If File(cFile)
    //Lendo o arquivo com XMLParser (lê a string), caso queira ler o arquivo direto, utilize o XMLParserFile (o arquivo deve estar dentro da system)
    oLido := XmlParser(MemoRead(cFile), cReplace, @cErros, @cAvisos)

    //Se tiver erros, mostra ao usuário
    If !Empty(cErros)
    Aviso(‘Atenção’, “Erros: “+cErros, {‘Ok’}, 03)
    EndIf

    //Se tiver avisos, mostra ao usuário
    If !Empty(cAvisos)
    Aviso(‘Atenção’, “Avisos: “+cAvisos, {‘Ok’}, 03)
    EndIf

    //Montando a Mensagem, data e hora
    //cMsg := “Data: “+oLido:_Dados:_Data:Text + Chr(13)+Chr(10)
    //cMsg := “Hora: “+oLido:_Dados:_Hora:Text + Chr(13)+Chr(10)

    //Percorrendo os produtos
    oProds := oLido:_Dados:_Produtos:_Produto
    For nAtual := 1 To Len(oProds)

    DEFINE DIALOG oDlg TITLE “Leitura do XML -> Monitor XML Logithink” FROM 180,180 TO 550,700 PIXEL

    // Vetor com elementos do Browse
    aBrowse := { {.T.,’Código 001′,’Descrição’,’Valor’,111.11}}
    //{.F.,’Código 002′,’Descrição’,’Valor’,222.22},;
    //{.T.,’Código 003′,’Descrição’,’Valor’,333.33} }

    // Cria Browse
    oBrowse := TCBrowse():New( 01 , 01, 260, 156,, {”,’Codigo’,’Descrição’,’Valor’},{20,50,50,50}, oDlg,,,,,{||},,,,,,,.F.,,.T.,,.F.,,, )

    // Seta vetor para a browse
    oBrowse:SetArray(aBrowse)

    // Monta a linha a ser exibina no Browse
    aBrowse := {{oAzul,oProds[nAtual]:_Codigo:Text,oProds[nAtual]:_Descricao:Text+”, “,Chr(13)+Chr(10)}}

    //{oVerde,”Endereço”,”Rua A, 123 casa 4″},;
    //{oVermelho,”Telefone”,”(21) 99583-1283″}}

    oBrowse:bLine := {||{ aBrowse[oBrowse:nAt,01],;
    aBrowse[oBrowse:nAt,02],;
    aBrowse[oBrowse:nAt,03] } }

    //{||{ If(aBrowse[oBrowse:nAt,01],”ID: “+oProds[nAtual]:_ID,),;
    //aBrowse[oBrowse:nAt,02],”Código: “+oProds[nAtual]:_Codigo,;
    //aBrowse[oBrowse:nAt,03] “Descrição: “+oProds[nAtual]:_Descricao } }
    //Transform(aBrowse) } }
    //Transform(aBrowse[oBrowse:nAT,04],’@E 99,999,999,999.99′) } }

    // Evento de clique no cabeçalho da browse
    oBrowse:bHeaderClick := {|o, nCol| alert(‘bHeaderClick’) }

    // Evento de duplo click na celula
    oBrowse:bLDblClick := {|| alert(‘bLDblClick’) }

    ACTIVATE DIALOG oDlg CENTERED
    Next

    //Mostrando a mensagem do xml lido
    Aviso(‘Atenção’, cMsg, {‘Ok’}, 03)
    EndIf
    Return

    • Bom dia Davi, tudo sim graças a Deus e você?
      Certo, no caso como você esta usando a função MemoRead, ela tem uma limitação por padrão de 64 kb.
      Você terá que ver se daria para aumentar com MaxStringSize e se não der, usar no lugar a FWFileReader com o método FullRead.

      • Davi Miranda disse:

        Boa tarde,
        Como vai?

        O estranho é que ele traz somente os pedidos e codigo do XML e não traz todos os itens em uma tela só, tem que fechar a tela para aparecer outra com o pedido da nota, se tiver 10 itens são 10 vezes clicando em fechar e uma tela nova aparecendo, mesmo eu colocando o código AddLine()

  5. Davi Miranda disse:

    Boa tarde,
    Como vai?

    Ao realizar a função como o exemplo ele da este erro.log

    THREAD ERROR ([1508], davic, DESKTOP-JEQGVNG) 22/12/2021 11:49:23
    invalid property _DADOS on FLEXML(MONNFPXB.PRW) 22/12/2021 11:44:35 line : 137

    Oque não deveria causar este erro,.

  6. Wanderson disse:

    Boa Tarde Daniel tudo bem,

    Baixei seu exemplo para ler os produtos de um XML e aqui não esta dando certo, ele gera o seguinte erro:

    invalid property _DADOS , olhando dentro do meu xml que é de uma nota fiscal normal não existe este _DADOS

    • Bom dia Wanderson, tudo joia?

      Você precisa usar o nome das tags que existam dentro do seu XML.

      Note no nosso exemplo, que na linha 63 e na linha 78, ele abre e fecha a tag chamada “dados”, e ai quando vamos trabalhar com o XML com o _ antes, fica _DADOS. Por isso que usamos essa tag (pois existe no nosso XML de exemplo).

      No seu caso, você precisa ver o nome da tag em questão, e usar ela, supondo que ela se chame “produtos” você usaria _PRODUTOS.

      Um grande abraço.

Deixe uma resposta

Terminal de Informação