Amiguinho,
Conforme o arquivo que voce postou dá para identificar que a necessidade de se obter cada resultado pode ser obtido através identação do código.
A identação trabalha somando um contador a cada vez que se inicia uma tag e reduzindo este contador a cada vez que ele fecha.
E como produzir um contador destes para nivelar? Em primeiro lugar precisamos encontrar um algo que seja padrão. Em HTML ou XML encontramos o padrão "<" para inicio da tag e "</".
Não é uma regra mas as vezes encontramos algo como "</>". Abreviações que eu vejo como trabalho preguiçoso, mas que podemos identificar como NADA, ou perda de tempo de processamento e fósforo.
voltando à sua dúvida, veja um exemplo postado a zilhões de anos de nosso amigo TOYA:
Código: Selecionar todos
/*
* INDENTAK - Indentador alem dos 64Kb
* toya Analista de Sistemas
* toya@ieg.com.br
* http://www.toya.hpg.com.br/
Compilar
Clipper indentak
Rtlink fi indentak
*/
parameters mascara
if type("mascara")="U"
? "Indentador de arquivos HTML"
?
? "Sintaxe:"
?
? " indentak seuarquivo.HTML"
?
? "Tamanho m ximo do arquivo:"
?
? " ???????? kbytes"
?
quit
endif
vet := directory(mascara)
clea
for n = 1 to len(vet)
nomearq := vet[n, 1]
rename (nomearq) to indentak.tmp
fd := fcreate(nomearq,0)
if fd = -1
? 'Erro na criacao do arquivo '+nomearq
inkey(0)
rename indentak.tmp to (nomearq)
loop
endi
dente := 3
m_a001 := {}
aadd(m_a001,{'db_detalhe','C',250,00})
f_prn := 'indentak.$db'
dbcreate('&f_prn',m_a001)
use &f_prn new exclu alias db_relatorio
appe from indentak.tmp sdf
nl := reccount()
es := 0
dbgotop()
for a = 1 to nl
go a
linha := alltrim(db_detalhe)
linha2 := ""
if len(linha) # 0
pedaco := upper(substr(linha, 1, 4))
if pedaco="</UL" .or. pedaco="</TD" .or. pedaco="</TR"
es := es - dente
endif
pedaco := upper(substr(linha, 1, 5))
if pedaco="</DIV" // pedaco="</IMG" .or.
es := es - dente
endif
pedaco := upper(substr(linha, 1, 6))
if pedaco="</HEAD" .or. pedaco="</BODY"
es := es - dente
endif
pedaco := upper(substr(linha, 1, 7))
if pedaco="</TABLE" .or. pedaco="</TBODY"
es := es - dente
endif
linha2 := space(es) + linha
pedaco := upper(substr(linha, 1, 3))
if pedaco="<UL" .or. pedaco="<TD" .or. pedaco="<TR"
es := es + dente
endif
pedaco := upper(substr(linha, 1, 4))
if pedaco="<DIV" // pedaco="<IMG" .or.
es := es + dente
endif
pedaco := upper(substr(linha, 1, 5))
if pedaco="<HEAD" .or. pedaco="<BODY"
es := es + dente
endif
pedaco := upper(substr(linha, 1, 6))
if pedaco="<TABLE" .or. pedaco="<TBODY"
es := es + dente
endif
endif
? linha2
buffer := linha2 + chr(13) + chr(10)
fwrite(fd, buffer, Len(buffer))
next a
fclose(fd)
ferase('indentak.tmp')
ferase('indentak.$db')
next n
quit
Veja que ele utiliza padrão para o tratamento do código.
Tá mais voce falou em HASH, e estou falando em STRINGS e COMPARAÇÕES, mas HASH é isto. É a comparação de um dado de tamanho variável, mas quando encontrado de dá um ponto de partida ou inicio.
Tá mas um XML é parecido com um PLANO DE CONTAS, ééééééh! Cada nivel do plano de contas é escalonado portando ao encontrar "<", "<" e "<" tres vezes seguidas quer dizer que o ultimo é nivel 3 e, ou se encontra outro "<" para o nivel quatro ou encontra-se o "</" para voltar o contador ao nivel 2.
Voltando ao seu XML, vou mostrar como lê-lo usando DOM objects, que é o que eu uso:
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...
// Cria objeto principal
oteste := oXMLDoc:getElementsByTagName( "infNFe" )
// Captura informacoes de identificacao
M->fileNFE := cFileName(cEDXFile)
M->PROTOCOLO := CriticaTagName( "nProt", oXMLDoc, cXMLDoc ) // protocolo previamente incluso
M->MOTIVO := CriticaTagName( "xMotivo", oXMLDoc, cXMLDoc ) // motivo ou situacao do arquivo
M->NFENUM := val(CriticaTagName( "nNF", oXMLDoc, cXMLDoc )) // numero da nota fiscal eletronica
M->IDNATUREZA := val(CriticaTagName( "CFOP", oXMLDoc, cXMLDoc )) // codigo de natureza de operacao
M->NATUREZA := CriticaTagName( "natOp", oXMLDoc, cXMLDoc ) // descritivo da natureza de operacao
// Captura dados do destinatario
if _tipo_ = "E"
xDestEmit := oXMLDoc:getElementsByTagName( "emit" )
else
xDestEmit := oXMLDoc:getElementsByTagName( "dest" )
endif
// Pega XML do trecho
cXMLDestEmit := xDestEmit:Item(0):xml
Veja que utilizo
...:getElementsByTagName onde busco por nome da TAG o seu conteúdo que pode ser um trecho string ou um nivel interno de XML.
Na variável
xDestEmit, peguei tudo contido na TAG "emit",
xDestEmit := oXMLDoc:getElementsByTagName( "emit" ) e logo depois posso trabalhar com todo o conteúdo resultante, em formato XML,
cXMLDestEmit := xDestEmit:Item(0):xml
Tudo bem Rochinha, mas se eu tiver mais de um nó dentro de outro nó? Bastará obter a contagem de sub-nós:
Código: Selecionar todos
xProds := oXMLDoc:getElementsByTagName( "det" )
for iProds = 1 to xProds:length // length retorna quantidade a contar
// Pega o conteúdo de cada TAG contida dentro do nó.
oTAGProd := oXMLDoc:getElementsByTagName( "prod" )
oTAGImposto := oXMLDoc:getElementsByTagName( "imposto" )
oTAGPIS := oXMLDoc:getElementsByTagName( "PIS" )
oTAGCOFINS := oXMLDoc:getElementsByTagName( "COFINS" )
Veja que
xProds:length me retornou o numero de nós internos da TAG "det" e se dentro dela ainda contiver outros nós com sub-nós bastará trabalhar cada um.
É um trabalho difícl, mas saboroso.
Talvez, isto lhe sirva de modelo, já que em se tratando de uso da HBXML e HASH não encontrei nenhum código que me fosse alusivo a ponto de mudar meus paradigmas.