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.
Debugando o fonte, podemos ver a estrutura do nosso objeto.
E ao finalizar, é mostrado uma mensagem dos produtos encontrados no objeto.
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_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?
Bom dia Caio, tudo bem?
Você teria que aumentar o limite nativo de strings em AdvPL, para isso, mude o atributo MaxStringSize dentro do appserver.ini.
Abaixo o link de documentação do TDN:
http://tdn.totvs.com/pages/viewpage.action?pageId=161349793
Abraços.
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
Obrigado pela contribuição Súlivan.
Parabéns pelo desenvolvimento.
Grande abraço.
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
Bom dia Artur, tudo bem?
Você poderia tentar usar a classe FWFileWriter junto da função EncodeUTF8.
Talvez com essas duas, pode ser que dê certo.
Grande abraço.
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.
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()
Opa, bem graças a Deus e você?
Então o bug com MemoRead foi corrigido e no caso esse que você mencionou agora é outro?
Eu não consegui resolver a questão do MemoREad, eu coloquei a o Código MaxStringSize no lugar do MemoRead mas não foi, deu erro de função, e não consigo ler o xml todo, ele apenas está lendo o produto ao invés da chave, será que é algo que não coloquei? Eu estou aprendendo ainda rs…
Ta, vamos por partes…
O MaxStringSize é uma chave que você deve configurar dentro do appserver.ini – https://tdn.totvs.com/pages/viewpage.action?pageId=161349793
Tente fazer o teste aumentando, se mesmo assim não der, ai você terá que trocar a MemoRead por FWFileReader.
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,.
Boa tarde Davi, tudo sim graças a Deus e você?
No seu XML não tem a tag dados, então tem duas possibilidades:
1. realmente não era pra ter, e você não deveria usar o _dados no fonte
2. era pra ter e no xml não foi colocado essa tag
Grande abraço.
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.