Parseando um XML com o SAX

Guilherme Silveira

Aqui você vai aprender a navagar por um arquivo XML através do SAX, que faz o parsing de um XML por eventos.

Download do material relacionado ao tutorial



Arquivos Xml e Sax

Este tutorial visa ensinar a usar a api chamada de SAX, Simple Api for XML, que vem junto com o Java 1.4 e, portanto, você deve ter esta versão do SDK instalado.

Percorrida estas páginas, voce será capaz ler um arquivo XML e usar os dados que lhe interessa. É necessário que você saiba o que é XML e para que você vai utilizá-lo.

SAX e suas implementações: SAX é um conjunto de interfaces (uma api) que deve ser implementada para efetuar o parsing de arquivos xml. No momento o Java 1.4 ja vem com implementações da api SAX. Você pode encontrar, por exemplo, o Xerces e o Crimson do projeto jakarta e instalá-los separadamente caso possua uma versão anterior do java, porém precisará adaptar um pouco mais o código, que vai alem do objetivo desse tutorial.



O Parser
Para criar um objeto parser, utilizamos uma API da Sun que procura um parser adequado.

1 // cria a factory e o parser
2 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();


Então abrimos o arquivo XML para leitura:

1 // abre a conexao com o arquivo, buffer de 1 mega
2 InputSource input = new InputSource(arquivo);


Uma vez que a API do Sax é orientada a eventos, criamos um objeto que lida com os eventos e dizemos ao parser para utilizar esse Handler durante o parsing do arquivo xml.

1 // inicia o parsing
2 parser.parse(input, new XMLHandler());


Observação: caso você queira utilizar um parser de XML que monte uma árvore com todo o XML, você deve usar um parser do tipo DOM. Lembre-se que o DOM coloca tudo em memória, o que pode não ser eficiente para XMLs grandes (em business to business, XMLs chegam a 500 megabytes).

Agora falta criar a nossa classe XMLHandler. Ela vai recebendo chamadas de método a medida que o arquivo XML é parseado pelo SAXParser.


XMLHandler
As classes que lidam com os eventos da api do SAX devem extender a DefaultHandler.

Quem já trabalhou com apis de GUI tanto em C/C++, Visual Basic ou Java (swing/awt), sabe como uma API orientada a eventos funciona e tera mais facilidade para entender o funcionamento dessa classe que criaremos. Durante o parsing do XML, esta nossa classe receberá chamadas, de acordo com o que o parser encontrar!

A classe DefaultHandler tem os métodos startDocument, endDocument, startElement, endElement e characters sendo chamadas quando um documento comeca e termina, quando um elemento (tag) comeca e termina e quando algum texto eh encontrado em um elemento (tag).

Nota: O estranho, e o que torna a api do SAX rápida, é que o método characters pode ser chamado mais de uma vez pra mesma TAG. Isso acontece, por exemplo, quando o arquivo xml possui código unicode.

Devido a esse detalhe do método characters devemos entao manter uma variável local a classe, que se preenche a cada chamada deste método, e é liberada no momento que o método endElement eh chamado, daremos o nome de valorAtual a ela.

Temos também que manter uma variável com o "endereço" atual do parser na estrutura do arquivo XML, isto é, quando o arquivo estiver dentro da tag Compra->Produtos->Produto devemos saber que ele esta nesse tag, e não no Venda->Produtos->Produto. Portanto não adianta checarmos se uma tag possui o nome que desejamos, mas precisamos sim é checar se o "endereço" atual no arquivo xml é o que procuramos. Para isso criamos a variável galhoAtual contendo o galho de parsing atual.

1 class XMLHandler extends DefaultHandler {
2 
3     /** o galho atual */
4     private StringBuffer galhoAtual = new StringBuffer(200);
5 
6     /** o valor da tag atual */
7     private StringBuffer valorAtual = new StringBuffer(100);
8 
9 }


Então implementamos os métodos que serão chamados no começo e fim do documento:

1 /** comeca um documento novo */
2 public void startDocument() {
3     System.out.print("iniciando");
4 }
5 
6 /** termina o documento */
7 public void endDocument() {
8     System.out.print("\nterminando");
9 }


Dica: Neste momento você pode finalizar algum processo com os dados que processou do arquivo inteiro.


Parseando as tags
Agora implementamos o método que marca o início de uma nova Tag, primeiro setando o galhoAtual para seu novo valor e limpando o valorAtual.

Então imprimimos o nome do galho atual para ter alguma saáda do programa.

01 /** comeca uma tag nova */
02 public void startElement(
03     String uri,
04     String localName,
05     String tag,
06     Attributes atributos)
07     throws ParserException {
08 
09     // seta o galho atual
10     galhoAtual.append("/" + tag);
11 
12     // mostra a tag
13     System.out.print(
14         "\n<"
15             + galhoAtual.substring(1)
16             (atributos.getLength() != " +ATRIBUTOS" "")
17             ">");
18 
19     // limpa a tag atual
20     valorAtual.delete(0, valorAtual.length());
21 
22 }


E o método que trata o fim de uma Tag, onde mostramos o valor interno da Tag, limpamos ele e setamos o galhoAtual novamente.

01 /** termina uma tag */
02 public void endElement(String uri, String localName, String tag)
03     throws ParserException {
04 
05     // mostra o valor
06     System.out.print(valorAtual.toString().trim());
07     // e limpa
08     valorAtual.delete(0, valorAtual.length());
09 
10     // seta o galho atual
11     galhoAtual.delete(
12         galhoAtual.length() - tag.length() 1,
13         galhoAtual.length());
14 
15 }


Por fim precisamos implementar o método que trata os valores internos das Tags, que adiciona esse valor para o valorAtual, uma vez que essa função pode ser chamada infinitas vezes dentro de uma tag.

Isto é, se você tem um XML deste tipo:

<tag1>gui lherme</tag1>


Pode ser (não necessariamente), que você receba duas chamadas para o characters(): uma para "gui", e outra para " lherme", por exemplo. Isto acontece devido a otimização da API.

Dica: A utilização de um StringBuffer é sempre melhor, uma vez que buffers trabalham muito mais rápido que Strings.

1 /** recebe os dados de uma tag */
2 public void characters(char[] ch, int start, int length) {
3 
4     // adiciona ao valor atual
5     valorAtual.append(ch, start, length);
6 
7 }


O arquivo java que acompanha o tutorial pode ser compilado e rodado assim:

java ExemploSax arquivo.xml


Utilize-o para ver realmente como o SAX funciona. Dentro do .zip tem 2 arquivos XML de exemplo.




Apoiado e desenvolvido por Caelum Cursos Java - Copyright © 2001-2008 GUJ