Processamento de XML em Java com a API SAX

Veja neste artigo os conceitos básicos de funcionamento da API SAX em Java para processamento de arquivos XML.

1. Introdução

Dentre as muitas vantagens que a XML oferece aos desenvolvedores, uma das mais interessantes é o fato de que não precisamos “inventar” o nosso próprio código para realizar o processamento de documentos XML. Por exemplo: o desenvolvedor não precisa perder tempo inventando uma lógica (algoritmo) para varrer um documento XML, identificando a abertura e o fechamento de tags para assim poder obter o conteúdo texto compreendido entre essas tags. Ao invés disso, os programas que lidam com XML podem fazer uso de bibliotecas especialmente projetadas para processar este tipo de estrutura.

Este artigo aborda uma das mais importantes bibliotecas da XML, denominada SAX (Simple API for XML). O restante do artigo está dividido da seguinte forma. Na Seção 2 explicamos o princípio de funcionamento da API SAX. A seguir, na Seção 3, mostramos apresentamos um exemplo prático de utilização da mesma em um programa Java. Na Seção 4, discutimos algumas de suas vantagens e limitações. Por fim, na Seção 5 apresentamos alguns comentários adicionais para quem pretende aprofundar-se em XML e SAX.

2. SAX - Princípio de Funcionamento

O primeiro passo para que você possa começar a trabalhar com a API SAX consiste em entender o seu princípio de funcionamento. Uma forma simples de fazer isso é imaginar que programar utilizando a SAX é uma atividade similar a participar de um jogo de pingue-pongue, onde, a cada momento, a bolinha será rebatida por um dos dois jogadores de cada lado da mesa. Nesse caso, o jogador 1 é o desenvolvedor e o jogador 2 é o processador SAX. Com o auxílio da Figura 1, explicaremos esse conceito de forma mais clara. Essa figura ilustra a forma com que a API SAX realiza o processamento de um documento XML bem simples.

Figura 1: Documento XML e eventos disparados pelo SAX

O processamento de XML com SAX tem duas características interessantes: (i) produz um laço automático que varre o documento de início ao fim; e (ii) durante esse laço automático, dispara diversos eventos para possibilitar com que o desenvolvedor possa recuperar informações contidas no documento XML. Os passos a seguir mostram como funciona esse “jogo de pingue-pongue” entre o desenvolvedor e a API SAX.

  1. No início do jogo, a “bola está com a gente”, ou seja, nós desenvolvedores é que damos o “saque inicial”: em nosso programa, iremos indicar para o processador SAX o documento XML a ser processado e solicitaremos com que ele realize a operação de parsing (processamento).
  2. Iniciado o parsing, a “bola passa para o lado do SAX”: o processador SAX iniciará a operação de parsing. Essa operação produz um laço automático que varre o documento XML do início ao fim!!! Ou seja: você não precisa criar um loop para processar o documento XML, pois o SAX faz isso para você!
  3. Durante esse loop automático, todas as vezes que o SAX encontrar um evento relevante durante o processamento do documento, ele irá “rebater a bola” para o desenvolvedor, para que este possa tomar alguma ação. Mas o que significa “evento relevante”? É simplesmente uma parte do documento XML onde o desenvolvedor muito provavelmente precisará recuperar algum tipo de informação com o intuito de tratá-la em seu programa. Aqui estão alguns exemplos:
    • Início do documento
    • Início de uma tag,
    • Fechamento de uma tag
    • Valor entre compreendido entre uma tag de abertura e uma tag de fechamento.
    • Fim do documento.
  4. Em seu programa, o desenvolvedor deverá criar métodos para “rebater de volta a bola”, ou seja, métodos associados a cada evento disparado pelo processador SAX. É através do uso desses métodos - denominados métodos de call-back - que podemos recuperar as informações do XML e utilizá-las dentro do programa. Esses métodos devem possuir nomes padronizados, conforme veremos na seção a seguir.

3. SAX - Um Programa em Java

Nesta seção, apresentamos um programa Java que ilustra a utilização prática da API SAX. O programa realiza o processamento do documento XML com informações sobre países representado na Listagem 1.

Listagem 1: Documento XML de Países


<?xml version="1.0" encoding="ISO-8859-1"?>
<paises>
  <pais sigla="BR">
	<nome>Brasil</nome>
	<moeda>Real</moeda>
	<populacao>196655014</populacao>
  </pais>
  <pais sigla="CA">
	<nome>Canadá</nome>
	<moeda>Dólar canadense</moeda>
	<populacao>34349561</populacao>
  </pais>
  <pais sigla="MX">
	<nome>México</nome>
	<moeda>Peso mexicano</moeda>
	<populacao>114793341</populacao>
  </pais>
</paises>

O programa Java é apresentado na Listagem 2. A classe DevmediaSAX processa o documento XML com o uso da API SAX, recuperando as informações de cada país e as imprimindo na tela (usando uma linha para cada país). A explicação sobre o funcionamento do programa é apresentada detalhadamente logo após o código.

Listagem 2: Programa Java


import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * classe DevmediaSAX: processa o documento XML de países com o uso da API SAX,
 * recuperando as informações de cada país e as imprimindo na tela (usando uma
 * linha para cada país).
 * 
 * A classe "DevmediaSAX" é derivada da classe "DefaultHandler" da biblioteca
 * org.xml.sax.helpers.DefaultHandler. Isso faz com que "DevmediaSAX" “ganhe”
 * automaticamente um processador SAX com o comportamento default.
 * 
 * @author Eduardo Corrêa Gonçalves
 * 
 */

public class DevmediaSAX extends DefaultHandler {

	private String tagAtual;
	private String siglaAtual;

	/**
	 * construtor default
	 */
	public DevmediaSAX() {
		super();
	}

	/**
	 * Método que executa o parsing: laço automático que varre o documento de
	 * início ao fim, disparando eventos relevantes
	 * 
	 * @param pathArq
	 */
	public void fazerParsing(String pathArq) {

		// Passo 1: cria instância da classe SAXParser, através da factory
		// SAXParserFactory
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser saxParser;

		try {
			saxParser = factory.newSAXParser();

			// Passo 2: comanda o início do parsing
			saxParser.parse(pathArq, this); // o "this" indica que a própria
								// classe "DevmediaSAX" atuará como
								// gerenciadora de eventos SAX.

			// Passo 3: tratamento de exceções.
		} catch (ParserConfigurationException | SAXException | IOException e) {
			StringBuffer msg = new StringBuffer();
			msg.append("Erro:\n");
			msg.append(e.getMessage() + "\n");
			msg.append(e.toString());
			System.out.println(msg);
		}
	}

	// os métodos startDocument, endDocument, startElement, endElement e
	// characters, listados a seguir, representam os métodos de call-back da API
	// SAX

	/**
	 * evento startDocument do SAX. Disparado antes do processamento da primeira
	 * linha
	 */
	public void startDocument() {
		System.out.println("\nIniciando o Parsing...\n");
	}

	/**
	 * evento endDocument do SAX. Disparado depois do processamento da última
	 * linha
	 */
	public void endDocument() {
		System.out.println("\nFim do Parsing...");
	}

	/**
	 * evento startElement do SAX. disparado quando o processador SAX identifica
	 * a abertura de uma tag. Ele possibilita a captura do nome da tag e dos
	 * nomes e valores de todos os atributos associados a esta tag, caso eles
	 * existam.
	 */
	public void startElement(String uri, String localName, String qName,
			Attributes atts) {

		// recupera o nome da tag atual
		tagAtual = qName;

		// se a tag for "<pais>", recupera o valor do atributo "sigla"
		if (qName.compareTo("pais") == 0) {
			siglaAtual = atts.getValue(0);
		}
	}

	/**
	 * evento endElement do SAX. Disparado quando o processador SAX identifica o
	 * fechamento de uma tag (ex: </nome>)
	 */
	public void endElement(String uri, String localName, String qName)
			throws SAXException {

		tagAtual = "";
	}

	/**
	 * evento characters do SAX. É onde podemos recuperar as informações texto
	 * contidas no documento XML (textos contidos entre tags). Neste exemplo,
	 * recuperamos os nomes dos países, a população e a moeda
	 * 
	 */
	public void characters(char[] ch, int start, int length)
			throws SAXException {

		String texto = new String(ch, start, length);

		// ------------------------------------------------------------
		// --- TRATAMENTO DAS INFORMAÇÕES DE ACORDO COM A TAG ATUAL ---
		// ------------------------------------------------------------

		if (tagAtual.compareTo("nome") == 0) {

			System.out.print(texto + " - SIGLA: " + siglaAtual);
		}

		if (tagAtual.compareTo("moeda") == 0) {

			System.out.print(" - MOEDA: " + texto);
		}

		if (tagAtual.compareTo("populacao") == 0) {

			System.out.println(" - POPULACAO: " + texto);
		}
	}

	/**
	 * Este é o saque inicial do jogo!! Recebe o nome o nome do arquivo XML de
	 * entrada, instancia um objeto da classe DevmediaSAX (myDevSax) e chama o
	 * método “fazerParsing” deste objeto
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {

		if (args.length != 1) {
			System.err.println("ERRO: modo de chamar 'PaisesSAX nome_do_xml'");
			System.exit(1);
		}

		DevmediaSAX myDevSax = new DevmediaSAX();
		myDevSax.fazerParsing(args[0]);
	}

}

Para executar o programa, inicialmente salve o arquivo XML de países (Listagem 1) em uma pasta de seu computador. Supondo que você salvou o arquivo com o nome “paises.xml” na mesma pasta do programa DevmediaSAX, bastará fazer a seguinte chamada: java DevmediaSAX paises.xml.

A explicação detalhada sobre o o programa é apresentada a seguir:

A Figura 2 mostra o resultado da execução.

Figura 2: Resultado da Execução

4. SAX - Vantagens e Limitações

A principal característica positiva da API SAX é a sua eficiência. É exatamente por esta razão que ela se tornou a principal API para a manipulação de documentos XML de tamanho grande. Ao contrário do que ocorre com a API DOM (outra API clássica para a manipulação de XML), quando trabalhamos com SAX o documento não é importado para a memória. O que é o processador SAX faz é simplesmente implementar um laço que varre o documento do início ao fim e dispara eventos sempre que um “trecho interessante” do documento é encontrado. O SAX foi exatamente criado com esse propósito: fazer o processamento de XML sem a necessidade de importar os dados para a memória (ou seja, o SAX contorna a deficiência que o modelo DOM possui para lidar com documentos muito grandes).

Embora eficiente, o esquema utilizado pelo SAX possui limitações. Alguns exemplos: durante o parsing, você não pode voltar para seções já processadas do documento. O processador SAX, em seu loop automático, anda apenas para frente. Além disso, a API SAX serve apenas para ler documentos XML e não para atualizá-los! Por sua vez, com o uso da API DOM é possível navegar em diferentes sentidos do XML e alterar o conteúdo do documento (mudar texto, remover elementos, inserir elementos, etc.). Na prática, tanto os modelos SAX e DOM são complementares. Dependendo do tamanho do documento e do problema que você deseja resolver você escolherá uma das duas API’s.

5. Comentários Finais

Este artigo apresenta apenas conceitos introdutórios sobre o uso da API SAX em programas Java. Apresentamos simplesmente uma “receita de bolo” para você manipular arquivos XML utilizando o modelo SAX. Como quase tudo ligado à XML, SAX é um tema rico e repleto de conceitos importantes. Alguns exemplos são: parsing com validação (uso de DTD’s), tratamento de caracteres especiais no texto (ex: texto com quebra de linha entre tags de abertura e fechamento), uso de namespaces, gerenciamento de exceções, tratamento de atributos (nosso documento exemplo contém “sigla” como único atributo), etc.

As referências básicas utilizadas para a elaboração deste texto foram as notas de aula da professora Vanessa Braganholo (IC/UFF). A Figura 1 foi adaptada de um exemplo mostrado no artigo “Desmistificando XML: da pesquisa à prática industrial”, de Mirella Moro e Vanessa Braganholo.

Artigos relacionados