Lendo um arquivo XML com XPath

Author PerlBrasil Category , ,

A leitura de arquivos XML não costumava ser algo trivial em diversas linguagens de programação. Com o tempo surgiram diversas bibliotecas para facilitar o trabalho com este tipo de arquivo.
A World Wide Web Consortium(http://www.w3.org/) padronizou uma linguagem para leitura de arquivos XML denominada "XML Path Language", ou simplesmente XPath(http://www.w3.org/TR/xpath/). Antes de falar da implementação da XPath em Perl vamos entender alguns conceitos importantes sobre este padrão. Vamos tomar como exemplo o seguinte arquivo XML:

<Biblioteca>
   
<Livro ISBN="1234567" Ano="2000">
        <Paginas>300</Paginas>
        <Titulo>A arte de programar em meia-hora</Titulo>
        <Exemplares>
            <Codigo>1</Codigo>
            <Codigo>2</Codigo>
        </Exemplares>
    </Livro>
    <Livro ISBN="7654321" Ano="1710">
        <Paginas>500</Paginas>
        <Titulo>Aprenda a usar o ábaco em 21 dias</Titulo>
        <Exemplares>
            <Codigo>10</Codigo>
            <Codigo>20</Codigo>
            <Codigo>30</Codigo>
            <Codigo>40</Codigo>
        </Exemplares>
    </Livro>
</Biblioteca>

A XPath prega que um arquivo XML deve ser representado basicamente como uma árvore de Nós, Atributos, Valores e Caminhos.
Nós: As tags do nosso documento. Na figura a seguir podemos ver os nós sendo representados em uma estrutura de árvore:
Atributos: São os atributos internos aos nós. No nó 'Livro', por exemplo, nós temos os atributos 'ISBN' e 'Ano'.
Valores: No contexto de atributo um valor é aquilo que aparece logo depois do sinal de '='. No contexto de nó, um valor é aquele entre a abertura e fechamento de uma tag.
Caminhos: São os "endereços" de cada nó representados na estrutura hierárquica a partir do nó principal. Por exemplo: '//Biblioteca/Livro' representa o endereço do nó Livro no XML. Vejamos outros caminhos possíveis na nossa árvore:


A linguagem XPath determina outra gama de conceitos que não é o foco do nosso post, mas se você se interessar acesse o link http://www.w3.org/TR/xpath/.

A implementação da XPath em Perl está contida no módulo XML::XPath, que você pode instalar diretamente via CPAN ou acessando o link http://search.cpan.org/~msergeant/XML-XPath-1.13/XPath.pm. Para criar um objeto do tipo XML::XPath, fazemos:

my $xpath XML::XPath->new(filename=>$inputFile);

E para procurar por determinado nó, fazemos:

my $nodeset = $xpath->find('//Biblioteca/Livro');

O método find do objeto XML::XPath retornará um conjunto de nós de acordo com o caminho que lhe for passado. No nosso caso será retornado um conjunto de nós 'Livro'. O módulo XML::XPath encapsula o conjunto de nós em um objeto do tipo XML::XPath::NodeSet.
Para iterar no conjunto de nós, o objeto XML::XPath::NodeSet possui o método get_nodelist. Assim, para iterar pelos nós 'Livro' retornados, fazemos:

foreach my $context ($nodeset->get_nodelist) { }

Onde a variável $context a cada iteração estará apontando para o novo 'Livro' pesquisado. Para recuperar os valores dos atributos e nós filhos de cada livro iterado, fazemos:

foreach my $context ($nodeset->get_nodelist) {
    my $isbn = $xpath->findvalue('@ISBN', $context);
    my $ano = $xpath->findvalue('@Ano', $context);
    my $paginas = $xpath->findvalue('Paginas', $context);
    my $titulo = $xpath->findvalue('Titulo', $context);
    my $exemplares = $xpath->find('Exemplares/Codigo', $context);
    foreach my $newcontext ($exemplares->get_nodelist){
        my $codigo = $xpath->findvalue('.', $newcontext);
    }
}

Aqui vale ressaltar que atributos(ISBN e Ano no nosso exemplo) de nós são recuperados somente com o sinal de '@' antes do nome do atributo. Os nós-folhas 'Paginas' e 'Titulo' aparecem somente uma vez em cada livro, logo, podemos pegar o valor diretamente através do método findvalue. No caso dos exemplares, podem existir diversos códigos, por isso tivemos que iterar pelo conjunto de nós retornados através do método find pertencente ao objeto XML::XPath.

A diferença básica do método find e o método findvalue é que o primeiro retorna o conjunto de nós encapsulado em um objeto XML::XPath::NodeSet, o qual poderá ser iterado via método get_nodelist, já o método findvalue retorna o conjunto de nós em uma String encapsulada no objeto XML::XPath::Literal, o qual obviamente não poderá ser iterado.

Aqui vai o exemplo completo de um programa para parsear o arquivo xml mostrado no início do post:

#!/usr/bin/perl

use XML::XPath;
my $inputFile = shift;
die("Faltou passar o nome do arquivo!") unless defined($inputFile);
my $xpath = XML:: XPath->new(filename => $inputFile);
my $nodeset = $xpath->find('//Biblioteca/Livro');
foreach my $context ($nodeset->get_nodelist) {
    print "----------------------------------------------------\n";
    my $isbn = $xpath->findvalue('@ISBN', $context);
    print "ISBN: $isbn\n";
    my $ano = $xpath->findvalue('@Ano', $context);
    print "Ano de Publicação: $ano\n";
    my $paginas = $xpath->findvalue('Paginas', $context);
    print "Quantidade de Páginas: $paginas\n";
    my $titulo = $xpath->findvalue('Titulo', $context);
    print "Título: $titulo\n";
    my $exemplares = $xpath->find('Exemplares/Codigo', $context);
    print "Exemplares disponíveis:\n";
    foreach my $newcontext ($exemplares->get_nodelist){
        my $codigo = $xpath->findvalue('.', $newcontext);
        print "\tCódigo: $codigo\n";
    }
}
print "----------------------------------------------------\n";


O módulo XML::XPath é muito simples de ser utilizado, apesar da complexidade da estrutura interna de objetos que são criados para representar o XML. Esta complexa estrutura torna caro sua utilização, visto que para XML grandes o tempo de processamento e de memória utilizada serão muito grandes.

Existem outros módulos para leitura de XML em Perl que são computacionalmente mais baratos, mas que não implementam o padrão XPath, como o XML::Simple. No próximo post falarei um pouco mais sobre o XML::Simple.

Abraços.

0 comentários:

Postar um comentário

Theme by New wp themes | Bloggerized by Dhampire