sábado, 14 de abril de 2007

Processando XML com Lua

Lua O Projeto Kepler oferece um módulo de interface com Expat, um processador XML. O módulo se chama LuaExpat.

Na verdade o módulo oferece diretamente apenas uma função: lxp.new(), que retorna um objeto processador de XML (parser). Esta função recebe como parâmetro uma tabela especial de callbacks, que são as funções responsáveis por tratar os elementos XML.

Se for passado como parâmetro uma tabela vazia, o parser apenas verificará a integridade do código XML.

Portanto, para usar LuaExpat para tratar XML, é preciso conhecer duas coisas: os callbacks e o parser.

Callbacks


Na tabela de callbacks, as chaves devem possuir nomes específicos que indicam em que caso cada callback será usado. Os valores são funções: os callbacks.

Há toda uma lista de callbacks (neste momento em que escrevo há quinze callbacks). Vamos dar uma olhadinha apenas em três principais: StartElement, EndElement e CharacterData.


StartElement é chamado quando é encontrada a abertura de um elemento (tag), por exemplo <xhtml:div id="main">. A função possui três argumentos: parser, elementName (nome do elemento) e attributes (atributos).

O primeiro argumento recebe o próprio parser.

O segundo, elementName, recebe o nome do elemento (no exemplo, xhtml:div).

O terceiro, attributes, recebe uma tabela com os atributos, tanto de forma indexada quanto associativa. Assim, no exemplo (<xhtml:div id="main">):
{
[1] = "main";
id = "main"
}



EndElement é chamado quando é encontrado o fechamento de um elemento, por exemplo </xhtml:div>. A função possui dois argumentos: parser e elementName (nome do elemento).


Quando é encontrado um elemento simples (<elem></elem> ou <elem/>), é chamado o callback StartElement e imediatamente o callback EndElement.


CharacterData é chamado quando é encontrada uma string CDATA (conteúdo de um elemento). A função recebe dois argumentos: parser e string (o texto).

Parser


O parser é criado pela função lxp.new(), que recebe a tabela de callbacks como parâmetro.

O parser possui diversos métodos, sendo os principais parse() (que processa uma string como parte do documento XML) e close() (método de chamada obrigatória que fecha o parser).

O método parse() deve ser chamado sem parâmetros para indicar o fim do documento.

A cada chamada de parse() são retornados cinco valores:
  1. true se correu bem ou nil se ocorreu algum erro;
  2. nil se correu bem ou uma mensagem de erro no caso de erro;
  3. número da linha ou nil;
  4. número da coluna ou nil;
  5. posição absoluta ou nil;


A última chamada (sem parâmetros) retornará true se a estrutura XML estiver bem formada ou nil e uma mensagem de erro se tiver ocorrido algum erro em algum momento.


Vamos a um exemplo:
require "lxp"

local fd, l, st, erro
local contador = 0
local p = lxp.new {
StartElement = function (self, nome, atributos)
io.write("+ ", (" "):rep(contador), nome, "\n")
contador = contador + 1
end,

EndElement = function (self, nome)
contador = contador - 1
io.write("- ", (" "):rep(contador), nome, "\n")
end,

CharacterData = function (self, texto)
io.write("* ", (" "):rep(contador + 1), texto, "\n")
end,
}

-- O Leonardo percebeu um erro meu aqui: é arg, não argv
fd = io.input(arg[1])

for l in fd:lines() do
p:parse(l .. "\n")
end

fd:close()
st, erro = p:parse()
p:close()

if not st then
print("Ocorreu o seguinte erro:", erro)
end


Salve este código no arquivo xmlparser.lua. Pegue um arquivo XML qualquer e execute:
bash$ lua xmlparser.lua nome-do-arquivo.xml


Se não houver nenhum disponível, use este:
<?xml version="1.0"?>
<elem1>
texto
<elem2/>
mais texto
</elem1>


Para saber mais: LuaExpat.

[]'s
Rodrigo Cacilhas
blog comments powered by Disqus