Utilizando XMLDOM para ler nós em XMLs complexos

Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.

Moderador: Moderadores

Avatar do usuário
rochinha
Administrador
Administrador
Mensagens: 4664
Registrado em: 18 Ago 2003 20:43
Localização: São Paulo - Brasil
Contato:

Utilizando XMLDOM para ler nós em XMLs complexos

Mensagem por rochinha »

Amiguinhos,

Embora nos ultimos tempos tenha sido dificil de podermos em nossos sistemas puxar os XMLs direto da receita e fazendo-se valer a lei de que o emissor é

obrigado a enviar o XML ao seu cliente, cabe à nós receber este arquivo e armazená-lo em local ou pasta que nossos sistemas possam catalogar e manipular.

Para criarmos um XML, não existe segredo, basta umar concatenação e umas funções de escrita e pronto.

Para recuperar o conteúdo é outra história.

No meu caso, andei por tempos batendo a cabeça para recuperar dados em XMLs e muitas vezes algo falhava.

Um exemplo é o caso da busca de tags em nós onde, hora existem, ora não, exemplo:

Dois itens na sessão produtos onde em um exista a tag IPI em apenas um dos conjuntos.

Buscar por AT(), RAT(), $ ou outra função de pesquisa de string nos faz rodar demais.

No meu caso, desde o inicio no trato com XML sempre apostei no uso dos motores do S.O. para este intuito em conjunto com OLE.

Código: Selecionar todos

   cEDXFile   := "35..-nfe.xml"

   // Caminhos
   cPathDados := "C:\Sistema"
   cEDXFile   := cPathDados+"\NF-e\remessa\ent\"+cFileName( cEDXFile )

   // Puxa o conteúdo do arquivo para manipulação
   cXMLDoc    := MemoRead( cEDXFile ) 
No trecho acima faço a leitura do arquivo que irei manipular usando funções nativas.

Código: Selecionar todos

   // Usa motor XML da Microsoft
   oXMLDoc    := TOLEAUTO():New( "Microsoft.XMLDOM" )
   oXMLDoc:async := .f. 

   lSuccess   := oXMLDoc:load( cEDXFile )
   if lSuccess // se abriu com sucesso...
Após faço uso de OLE para acionar o motor de onde farei uso dos métodos e propriedades.

Para capturar dados de uma tag simples bastaria:

Código: Selecionar todos

oXMLDoc:getElementsByTagName( "xNome" ):Text
Para capturar um conjunto mais complexo ou repleto de subconjuntos seria necessário algumas linhas a mais:

Código: Selecionar todos

   // Traz todo o conjunto para dentro de um unico objeto
   xProds := oXMLDoc:getElementsByTagName( "det" )

   // Faço uma contagem de quantidade de subconjuntos
   nProds := xProds:length // length retorna quantidade a contar

   // Executo o laço
   for iProds = 1 to nProds

       // Para cada subconjunto/nó mais interno crio o suporte
       oTAGProd     := oXMLDoc:getElementsByTagName( "prod" )      
       oTAGImposto  := oXMLDoc:getElementsByTagName( "imposto" )      
       oTAGPIS      := oXMLDoc:getElementsByTagName( "PIS" )      
       oTAGCOFINS   := oXMLDoc:getElementsByTagName( "COFINS" )      

       // Pego o XML somente do conjunto que desejo
       cXMLProd     := oTAGProd:Item(iProds-1):xml
       cXMLImposto  := oTAGImposto:Item(iProds-1):xml
       cXMLPIS      := oTAGPIS:Item(iProds-1):xml
       cXMLCOFINS   := oTAGCOFINS:Item(iProds-1):xml
Apenas para evitar erros durante a leitura e angariação dos dados faço algumas criticas:

Código: Selecionar todos

       // Caso exista a tag processo/pego seu conteúdo
       lRESUMIDO    := iif( ValidaXMLField( "cProd" , cXMLProd ), .t., .f. )
       lDESCRICAO   := iif( ValidaXMLField( "xProd" , cXMLProd ), .t., .f. )
Após criticar se vou ou não acionar o objeto que preciso, pego o seu conteúdo, caso não, nem tento utilizar pois o objeto nem existirá para critica:

Código: Selecionar todos

       M->RESUMIDO  := iif( lRESUMIDO  , oXMLDoc:getElementsByTagName( "cProd" ):Item(iProds-1):Text , "" ) 
       M->DESCRICAO := iif( lDESCRICAO , oXMLDoc:getElementsByTagName( "xProd" ):Item(iProds-1):Text , "" ) 
Analisando o trecho abaixo veremos como pegar cada nó isoladamente para poder trabalhar sobre ele:

Código: Selecionar todos

		<det nItem="1">
			<prod>
				<cProd>120088</cProd>
				<cEAN/>
				<xProd>PORTA CARTAO</xProd>
				<NCM>39095029</NCM>
				<CFOP>5101</CFOP>
				<uCom>BL</uCom>
				<qCom>1.0000</qCom>
				<vUnCom>3597.6000</vUnCom>
				<vProd>3597.60</vProd>
				<cEANTrib/>
				<uTrib>BL</uTrib>
				<qTrib>1.0000</qTrib>
				<vUnTrib>3597.6000</vUnTrib>
				<indTot>1</indTot>
			</prod>
			<imposto>
				<ICMS>
					<ICMSSN202>
						<orig>0</orig>
						<CSOSN>202</CSOSN>
						<mobBCST>4</mobBCST>
						<pMVAST>57.0000</pMVAST>
						<pRedBCST>0.00</pRedBCST>
						<vBCST>5648.23</vBCST>
						<pICMSST>12.0000</pICMSST>
						<vICMSST>109.01</vICMSST>
					</ICMSSN202>
				</ICMS>
				<IPI>
					<IPITrib>
						<CST>53</CST>
						<vBC>0.00</vBC>
						<pIPI>0.00</pIPI>
						<vIPI>0.00</vIPI>
					</IPITrib>
				</IPI>
				<PIS>
					<PISOutr>
						<CST>99</CST>
						<vBC>0.00</vBC>
						<pPIS>0.00</pPIS>
						<vPIS>0.00</vPIS>
					</PISOutr>
				</PIS>
				<COFINS>
					<COFINSOutr>
						<CST>99</CST>
						<vBC>0.00</vBC>
						<pCOFINS>0.00</pCOFINS>
						<vCOFINS>0.00</vCOFINS>
					</COFINSOutr>
				</COFINS>
			</imposto>
		</det>
Ao executar oTAGProd := oXMLDoc:getElementsByTagName( "prod" ) trago somente o conteúdo do nó prod:

Código: Selecionar todos

			<prod>
				<cProd>120088</cProd>
				<cEAN/>
				<xProd>PORTA CARTAO</xProd>
				<NCM>39095029</NCM>
				<CFOP>5101</CFOP>
				<uCom>BL</uCom>
				<qCom>1.0000</qCom>
				<vUnCom>3597.6000</vUnCom>
				<vProd>3597.60</vProd>
				<cEANTrib/>
				<uTrib>BL</uTrib>
				<qTrib>1.0000</qTrib>
				<vUnTrib>3597.6000</vUnTrib>
				<indTot>1</indTot>
			</prod>
Ao executar oTAGImposto := oXMLDoc:getElementsByTagName( "imposto" ) trago somente o conteúdo do nó imposto:

Código: Selecionar todos

			<imposto>
				<ICMS>
                                     ...
				</ICMS>
				<IPI>
                                     ...
				</IPI>
				<PIS>
                                     ...
				</PIS>
				<COFINS>
                                     ...
				</COFINS>
			</imposto>

Ao executar oTAGPIS := oXMLDoc:getElementsByTagName( "PIS" ) trago somente o conteúdo do nó PIS:

Código: Selecionar todos

				<PIS>
					<PISOutr>
						<CST>99</CST>
						<vBC>0.00</vBC>
						<pPIS>0.00</pPIS>
						<vPIS>0.00</vPIS>
					</PISOutr>
				</PIS>
O conteúdo trazido por ..objeto..:Item(iProds-1):xml mostra somente o trecho dentro o objeto de questão:

Lógico que o que foi aqui apresentado é somente um rascunho esclarecedor mas parcial do que seria preciso para captar adequadamente todo o conteúdo de um XML como o de uma nota fiscal eletronica. Mas a idéia é conhecer como chegar mais fundo nos XML mais complexos.

A postagem apresentada tratou somente da obtenção de trechos em nós e não tratou atributos.

Para isto bastará buscar informações sobre os métodos attribute do objeto XMLDOM

Algumas funções usadas:

Código: Selecionar todos

Function CriticaTagName( _tagName_, oXMLDoc, cXMLDoc )
   if upper( "<"+_tagName_+">" ) $ upper( cXMLDoc )
      _cTagName_ := oXMLDoc:getElementsByTagName( _tagName_ ):Item(0):Text
   else
      _cTagName_ := ""
   endif
   return _cTagName_
   
function ValidaXMLField( _XMLField_, _XMLFile_ )
   return iif( AT( "<"+_XMLField_+">", _XMLFile_ ) > 0, .t., .f. )

function XMLTagInsert( cTag, cConteudo, nSpaces )
   DEFAULT nSpaces := 0
   return space(nSpaces)+[<]+cTag+[>] + cConteudo + [</]+cTag+[>]

function getXMLValue( cXML, cTAG )
   LOCAL nPosIni := At( [<]+cTag+[>], cXML ) + len( [<]+cTag+[>] )
   LOCAL nPosFim := At( [</]+cTag+[>], cXML ) - ( len([</]+cTag+[>]) + 1 )
   return substr( cXML, nPosIni, nPosFim-nPosIni )
Foram pelo menos duas semanas de ensaio e cobranças para colocar este post no ar.
OPS! LINK QUEBRADO? Veja ESTE TOPICO antes e caso não encontre ENVIE seu email com link do tópico para [url=mailto://fivolution@hotmail.com]fivolution@hotmail.com[/url]. Agradecido.

@braços : ? )

A justiça divina tarda mas não falha, enquanto que a justiça dos homens falha porque tarda.
Avatar do usuário
fladimir
Colaborador
Colaborador
Mensagens: 2445
Registrado em: 15 Nov 2006 20:21

Utilizando XMLDOM para ler nós em XMLs complexos

Mensagem por fladimir »

Rochinha obrigado pela contribuição, não sou xpert nesta área, mas fique com uma dúvida...

...não sei se vc conhece akele tópico q tem aki do Frazato q ele compartilhou um função chamada LerDANFE e tb a Pegadados... sem analisar a fundo, mas acredito q elas tb consigam manipular, pois vc informa a TAG q quer analisar ou no seu ponto de vista não?

Segue exemplo

[]´s
Sun Tzu há mais de três mil anos cita nas epígrafes de seu livro “A Arte da Guerra“:

“Concentre-se nos pontos fortes, reconheça as fraquezas, agarre as oportunidades e proteja-se contra as ameaças”.
“Se não é vantajoso, nunca envie suas tropas; se não lhe rende ganhos, nunca utilize seus homens; se não é uma situação perigosa, nunca lute uma batalha precipitada”
.


Até 2017    Desktop Console [ Legado ] Harbour | MinGW | DBF | CDX | FastReport | MySQL


Novos Projetos:

   Desktop Visual           Windev Desktop
   Celular Android/iOS   Windev Mobile
   WEB                            Windev Web


Sejamos gratos a Deus.
Avatar do usuário
rochinha
Administrador
Administrador
Mensagens: 4664
Registrado em: 18 Ago 2003 20:43
Localização: São Paulo - Brasil
Contato:

Utilizando XMLDOM para ler nós em XMLs complexos

Mensagem por rochinha »

Amiguinho,

Meu código se basea sim e pesquisa de string e realmente funcionava, mas a pesquisa poderia falhar se as tags e conjuntos não fossem realmente perfeitas.

Já peguei muitas notas onde o conteúdo era horripilante de se ver. Tinha conjunto com todas as tags, outros com metade e outros só com o minimo e o mais incrivel, às vezes fora de ordem, tipo cofins aparecendo antes do icms. Presumindo eu é claro que o xml tinha sido enviado antes da validação.

Imagine voce fazendo uma pesquisa por string onde os conjuntos de assemelhem a isto:

Código: Selecionar todos

      <detalhe>
            <produto=1>
                  <codigo>
                  <descricao>
                  <valor>
                  <icms>
                  <ipi>
            </produto>
            <produto=2>
                  <codigo>
                  <descricao>
                  <valor>
                  <iss>
            </produto>
            <produto=3>
                  <codigo>
                  <descricao>
                  <valor>
                  <icms>
                  <ipi>
            </produto>
      </detalhe>
Veja que no produto=2 não existe o nó icms e ipi e figura o iss, então voce precisa montar um código de leitura, ("mãe diná"), que preveja se existirá as tags e guarde o conteúdo capturado em variáveis pré-existentes.

Se voce simplemente usar uma função de busca, estando no item 2, poderá ocorrer de o item 2, assumir os conteúdos do item 3.

Eu tive muito problema em pegar dados de notas onde o ipi figurava nas mais variadas formas. Mas resolvi com muitos ifs...endif.

Depois quando precisei refazer os cálculos de CSOSN testar a criação e a puxada das notas vi que o buraco era mais embaixo. Precisava, criticar somente o bloco de texto que estava manipulando, desta forma eu poderia criticar individualmente cada trecho com o minimo de esforço.

Meus testes foram depois efetivados em notas com mais de 100 itens e os erros foram reduzidos a zero.

E pelo fato de não se considerar um expert voce poderá agregar este conhecimento, ir usando e adaptando e com certeza logo, logo, estará usando melhor as caracteristicas tão já impregnadas na maioria dos softwares.
OPS! LINK QUEBRADO? Veja ESTE TOPICO antes e caso não encontre ENVIE seu email com link do tópico para [url=mailto://fivolution@hotmail.com]fivolution@hotmail.com[/url]. Agradecido.

@braços : ? )

A justiça divina tarda mas não falha, enquanto que a justiça dos homens falha porque tarda.
Avatar do usuário
fladimir
Colaborador
Colaborador
Mensagens: 2445
Registrado em: 15 Nov 2006 20:21

Utilizando XMLDOM para ler nós em XMLs complexos

Mensagem por fladimir »

Entendi colega... obrigado pelos esclarecimentos...

[]´s
Sun Tzu há mais de três mil anos cita nas epígrafes de seu livro “A Arte da Guerra“:

“Concentre-se nos pontos fortes, reconheça as fraquezas, agarre as oportunidades e proteja-se contra as ameaças”.
“Se não é vantajoso, nunca envie suas tropas; se não lhe rende ganhos, nunca utilize seus homens; se não é uma situação perigosa, nunca lute uma batalha precipitada”
.


Até 2017    Desktop Console [ Legado ] Harbour | MinGW | DBF | CDX | FastReport | MySQL


Novos Projetos:

   Desktop Visual           Windev Desktop
   Celular Android/iOS   Windev Mobile
   WEB                            Windev Web


Sejamos gratos a Deus.
Imatech
Usuário Nível 3
Usuário Nível 3
Mensagens: 350
Registrado em: 24 Ago 2010 23:48
Localização: Goiânia-GO

Utilizando XMLDOM para ler nós em XMLs complexos

Mensagem por Imatech »

Olá Rochinha !

Podemos tambem obter os mesmos resultados com as Classes nativas do Harbour :)
*** Acredito inclusive ser + prático de se implementar...
*** Podemos utilizar: hbxml ou hbmxml

Segue exemplo Base:

Código: Selecionar todos


#include "hbxml.ch"

*-----------------------------------------------------------------------------*
* F_RM_Leitura_Nfe( cXML_Nfe )
*-----------------------------------------------------------------------------*
Function F_RM_Leitura_Nfe( cXML_Nfe )
  LOCAL oDoc := TXmlDocument():New( cXML_Nfe, HBXML_STYLE_NOESCAPE )
  LOCAL oBook, oIterator, oCurrent
  LOCAL lResult := .T.
  LOCAL bAux := .T.

  IF oDoc:nError != HBXML_ERROR_NONE
    Alert( 'ERRO NA ABERTURA DO ARQUIVO...;;' + cXML_Nfe )
  ELSE

    *-------------------------------------------------------------------*
    * DADOS DO DOCUMENTO
    *-------------------------------------------------------------------*
    oBook := oDoc:findfirst( "ide" )
    IF oBook == NIL
      Alert( 'ARQUIVO INVALIDO: IDENTIFICACAO DO DOCUMENTO...;;' + cXML_Nfe )
    ELSE

      oIterator := TXmlIterator():New( oBook )

      oCurrent := oIterator:Next()

      IF !( EMPTY( oCurrent:cData ) )
        * DATA DE EMISSÃO
        IF oCurrent:cName == 'dEmi'
          ? 'Save To...'
        ENDIF

        * DATA DE SAIDA
        IF oCurrent:cName == 'dSaiEnt'
          ? 'Save To...'
        ENDIF
      ENDIF

      *-------------------------------------------------------------------*
      * DADOS DO DESTINATARIO
      *-------------------------------------------------------------------*
      oBook := oDoc:findfirst( "dest" )
      IF oBook == NIL
        Alert( 'ARQUIVO INVALIDO: DADOS DO DESTINATARIO...;;' + cXML_Nfe )
      ELSE
        bAux := .T.
        oIterator := TXmlIterator():New( oBook )
        WHILE bAux
          oCurrent := oIterator:Next()
          IF oCurrent == NIL
            bAux := .F.
          ELSE
            IF !( EMPTY( oCurrent:cData ) )
              IF oCurrent:cName == 'CNPJ'
                ? 'Save To...'
              ENDIF
              IF oCurrent:cName == 'CPF'
                ? 'Save To...'
              ENDIF
            ENDIF
          ENDIF
        ENDDO
      ENDIF

      *-------------------------------------------------------------------*
      * DADOS DO EMITENTE
      *-------------------------------------------------------------------*
      oBook := oDoc:findfirst( "emit" )
      IF oBook == NIL
        Alert( 'ARQUIVO INVALIDO: DADOS DO EMITENTE...;;' + cXML_Nfe )
      ELSE
        bAux := .T.
        oIterator := TXmlIterator():New( oBook )
        WHILE bAux
          oCurrent := oIterator:Next()
          IF oCurrent == NIL
            bAux := .F.
          ELSE
            IF !( EMPTY( oCurrent:cData ) )
              IF oCurrent:cName == 'CNPJ'
                ? 'Save To...'
              ENDIF
              IF oCurrent:cName == 'xNome'
                ? 'Save To...'
              ENDIF
              IF oCurrent:cName == 'xLgr'
                ? 'Save To...'
              ENDIF
              IF oCurrent:cName == 'nro'
                ? 'Save To...'
              ENDIF
              IF oCurrent:cName == 'xCpl'
                ? 'Save To...'
              ENDIF
            ENDIF
          ENDIF
        ENDDO
      ENDIF
    ENDIF
  ENDIF

Return( lResult )
M., Ronaldo

by: IMATECH

IMATION TECNOLOGIA
Avatar do usuário
rochinha
Administrador
Administrador
Mensagens: 4664
Registrado em: 18 Ago 2003 20:43
Localização: São Paulo - Brasil
Contato:

Utilizando XMLDOM para ler nós em XMLs complexos

Mensagem por rochinha »

Amiguinho,

Isto hoje é possivel, apesar de minha implementação datar de 2006, quando tais recursos não estavam tão disseminados ou nem existiam a contento.

Alguma coisa existia em xHarbour, mas as incompatibilidades notórias nos faziam procurar por algo mais compatível.

Com a melhoria das classes do Harbour Tradicional e a retomada de sua manutenção podemos ter agora mais ferramentas para nossas implementações.

Tem tantas que nem eu conheço o uso de algumas.
OPS! LINK QUEBRADO? Veja ESTE TOPICO antes e caso não encontre ENVIE seu email com link do tópico para [url=mailto://fivolution@hotmail.com]fivolution@hotmail.com[/url]. Agradecido.

@braços : ? )

A justiça divina tarda mas não falha, enquanto que a justiça dos homens falha porque tarda.
Responder