Page 1 of 1

Problema com Parser XML

Posted: 07 Aug 2020, 14:12
by pedro45_vs
Boa tarde,

Estou com alguma dificuldade em parsear arquivos XML da Nota fiscal eletrônica. O que acontece é que há vários nodes em que não existe uma certa tag, como por exemplo, <vICMS>. Por isso eu preciso utilizar o método SelectSingleNodes.

O problema é que na hora de fazer o Loop, eu não consigo fazer o script resolver a váriavel para ir puxando os campos.

Code: Select all

FileRead, arq, *p65001 arq.xml

doc := ComObjCreate("Msxml2.DOMDocument.3.0")
doc.async := false
doc.loadXML(arq)

Valor_ICMS := doc.selectSingleNode("//det[0]/imposto/ICMS/*/vICMS") ; meu problema é não conseguir iterar para ir selecionando os próximo nodes, det[1], det[2]
Valor_ICMS := Valor_ICMS.text

MsgBox, % Valor_ICMS
Como faço para fazer um Loop para selecionar o det[0], det[1], det[2], etc...?

Obs. Eu até tentei o método selectNodes, mas aí quando um produto não tiver a chave, ele vai simplesmente pular ao invés de me retornar um valor vazio por exemplo.

Será que alguem sabe como posso resolver?

Re: Problema com Parser XML

Posted: 07 Aug 2020, 18:02
by Gio
Boa tarde Pedro45_vs.

Eu tenho alguns scripts que fazem exatamente isso (ler dados de XMLs de notas fiscais). Fiz da seguinte forma para entrar nos itens da nota:

1. Primeiro, crio uma variável para ser um contador de itens da nota e inicio ela com valor 0 (zero).
2. Depois, faço um Loop com 1000 iterações. Esse loop vai descobrir cada item da nota (o número 1000 não é exatamente aleatório: a nota fiscal eletrônica aceita no máximo 990 itens diferentes).
3. No corpo do loop, o código usa o método selectSingleNode para buscar os dados da tag <xProd> que esteja dentro da tag Det[%A_Index%]. Logo, esse código vai pegar um-a-um todos os valores de <xProd> dentro de <det[número]> que existirem no XML da nota (a tag <xProd> é obrigatória para todos os itens da nota fiscal e não pode conter valor em branco, senão a nota não foi aprovada).
4. Logo em seguida, ainda dentro do corpo do loop, testo o valor encontrado em cada iteração. Se não for em branco, aumento o contador de itens em 1. Se for em branco, termino o loop (porque na verdade não precisa iterar 1000 vezes toda vez).
5. A partir daí a variável do contador de itens vai ter a exata quantidade de itens da nota, então posso usar ela para buscar qualquer informação de item pela ordem do item. Também posso usar ela para criar outros loops que extraiam os dados específicos de cada item.
6. Portanto, caso um determinado item EXISTA e ao mesmo NÃO TENHA ESPECIFICAMENTE A TAG <vICMS>, podemos saber facilmente: se o número do det[] for menor que o total do contador de itens esse item existe. E se mesmo assim a tag vICMS retornar em branco, então de fato é um item que não tem a tag vICMS.

Exemplo:

Code: Select all

FileSelectFile, XML_FILE,,,Selecione os arquivo de XML da nota, Arquivos de XML (*.xml;)
If (ErrorLevel)
{
	msgbox, 0x10, Erro, Falha ao ler o arquivo. Por favor, reinicie o programa e selecione o arquivo XML novamente.
	SB_SetText("Aguardando XML")
	Return
}

FileRead, XML_FILE_CONTENTS, %XML_FILE%

If ((ErrorLevel) OR (XML_FILE_CONTENTS = ""))
{
	msgbox, 0x10, Erro, Falha ao ler o arquivo. Por favor, reinicie o programa e selecione o arquivo XML novamente.
	SB_SetText("Aguardando XML")
	Return
}

ns = xmlns=".*?"
xmldata := RegExReplace(XML_FILE_CONTENTS, ns, "")

xmlA := ComObjCreate("MSXML2.DOMDocument.6.0")
xmlA.async := false
xmlA.loadXML(xmldata)


TOTAL_DE_ITENS := 0
Loop 1000
{
	DESC_PROD := xmlA.selectSingleNode("//det[" . A_Index . "]/prod/xProd").text
	If !(DESC_PROD = "")
	{
		TOTAL_DE_ITENS++
	}
	else ; Esse ELSE é opcional, caso você queira terminar o loop no primeiro item cuja TAG não exista (ao invés de efetivamente iterar até 1000).
	{
		Break
	}
}

Loop % TOTAL_DE_ITENS ; A partir daqui eu posso usar a variável para indicar uma iteração por item da nota.
{
	PROD := CD_FABR_PROD := xmlA.selectSingleNode("//det[" . A_Index . "]/prod/xProd").text
	EAN_PROD := xmlA.selectSingleNode("//det[" . A_Index . "]/prod/cEAN").text
	VALOR_UNIT_NF := xmlA.selectSingleNode("//det[" . A_Index . "]/prod/vUnCom").text
	
	vICMS := xmlA.selectSingleNode("//det[" . A_Index . "]/imposto/ICMS/ICMS00/vICMS").text ; OBSERVAR SEMPRE O CAMINHO DAS TAGS!
	
	msgbox, 0, Aviso, Total de Itens: %TOTAL_DE_ITENS%`n`nItem %A_index%:`n`nDESCRIÇÃO: %PROD%`nEAN: %EAN_PROD%`nVALOR UNITARIO: %VALOR_UNIT_NF%`nvICMS: %vICMS%
}

Re: Problema com Parser XML

Posted: 08 Aug 2020, 10:32
by pedro45_vs
Em primeiro lugar gostaria de agradecer sua atenção.

Segundo, vc acredita que depois de chegar em casa, enquanto eu tomava um banho eu tive a seguinte ideia:

Usar o SelectNodes para selecionar todos os produtos e determinar a quantidade de vezes que o Loop será feito.
Depois criar uma variável para manipular a expressão XPath antes de aplicar o método selectSingleNode.
E não é que deu certo? :dance: Veja o que eu fiz:

Code: Select all

Node0 := doc.selectNodes("//det/prod") ; faço isso apenas para determinar a quantidade de Loops necessários para cada arquivo XML
For key in Node0 {

iNode1 := "//det[" (A_Index - 1) "]/imposto/ICMS/*/vICMS"  ; fazendo assim eu consigo resolver a expressão XPath antes de aplicar o método SelectSingleNodes
vICMS :=  doc.selectSingleNode(iNode1).text 

if (vICMS = "") {
vICMS := 0
}

MsgBox, %vICMS%
}

Ps. Engraçado como às vezes a gente empaca em um problema e depois, em momentos que a gente nem imagina, vem uma ideia para resolver. :lol:

Re: Problema com Parser XML

Posted: 23 May 2021, 09:58
by pedro45_vs
Coletar dados em arquivos XML da nota fiscal eletrônica é uma das tarefas mais úteis que meus Scripts fazem. Portanto encontrei uma forma mais simples de fazer isso com o código abaixo:

Code: Select all

doc := ComObjCreate("Msxml2.DOMDocument.3.0")
doc.async := false
doc.load(arq.xml)

For k, in doc.selectNodes("//det") ; este método seleciona o conjunto de nós que contém as informações de cada produto da nota fiscal
 {
        xProd       := k.selectSingleNode("./prod/xProd").text ; Utilizando a notação do ponto na expressão Xpath não é necessário escrever a expressão completa 
        NCM         := k.selectSingleNode("./prod/NCM").text
        CFOP        := k.selectSingleNode("./prod/CFOP").text
        CST           := k.selectSingleNode("./imposto/ICMS/*/CST | ./imposto/ICMS/*/CSOSN").text
        qCom        := Format("{:.2f}", k.selectSingleNode("./prod/qCom").text)
        vProd       := Format("{:.2f}", k.selectSingleNode("./prod/vProd").text) ; a função Format já retorna a quantidade em formato mais amigável
}