Análise de Documentos XML em Java: Comparativo entre DOM, SAX e Dom4j

A análise de documentos XML é uma tarefa fundamental em muitas aplicações Java, permitindo que os programas leiam, processem e manipulem dados estruturados. Existem diversas abordagens e bibliotecas para realizar essa operação, cada uma com suas características e casos de uso ideais.

Principais Métodos de Análise XML

No universo Java, os dois paradigmas mais comuns para análise de XML são o Document Object Model (DOM) e a Simple API for XML (SAX).

Document Object Model (DOM)

  • O DOM é uma recomendação do W3C que representa o documento XML como uma árvore de objetos na memória. Cada nó da árvore corresponde a uma parte do XML (elemento, atributo, texto, etc.).
  • Vantagens: Facilita a navegação bidirecional (para frente e para trás), permite modificação completa do documento (adicionar, remover, atualizar nós) e é mais intuitivo para manipulações complexas.
  • Desvantagens: Requer carregar o documento inteiro na memória antes do processamento, o que pode consumir muitos recursos para arquivos XML grandes.

Simple API for XML (SAX)

  • O SAX é uma API de processamento baseada em eventos. Em vez de carregar todo o documento, ele lê o XML sequencialmente e dispara eventos (como "início de elemento", "fim de elemento", "dados de caractere") conforme encontra as diferentes partes do documento.
  • Vantagens: É muito eficiente em termos de memória, pois não mantém o documento inteiro na memória, sendo ideal para arquivos XML grandes e para cenários onde apenas a leitura sequencial é necessária.
  • Desvantagens: Não permite navegação bidirecional ou modificação direta do documento. A lógica de processamento precisa ser implementada dentro dos manipuladores de eventos, o que pode ser mais complexo para certas tarefas.

Bibliotecas para Análise de XML em Java

JAXP (Java API for XML Processing)

O JAXP é parte integrante do Java SE e oferece interfaces padrão para trabalhar com DOM e SAX. Ele abstrai as implementações subjacentes dos parsers XML, permitindo que o desenvolvedor use diferentes provedores de parser sem alterar o código da aplicação.

Análise DOM com JAXP

Para analisar um documento XML usando a abordagem DOM com JAXP, seguimos um fluxo que envolve a criação de uma fábrica de construtores, um construtor de documentos e, finalmente, o próprio documento XML como um objeto Document. Este exemplo lê informações sobre livros de um arquivo XML.

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;

public class AnalisadorDomLivros {
    public static void main(String[] args) {
        try {
            // 1. Criar uma fábrica para obter um DocumentBuilder
            DocumentBuilderFactory fabricaConstrutor = DocumentBuilderFactory.newInstance();
            // 2. Obter um construtor de documentos a partir da fábrica
            DocumentBuilder construtorDocumento = fabricaConstrutor.newDocumentBuilder();
            // 3. Analisar o arquivo XML e obter o objeto Document
            // Certifique-se de que 'livros.xml' exista em 'src/main/resources/'
            Document documentoXml = construtorDocumento.parse(new File("src/main/resources/livros.xml"));

            // Normalizar o documento é útil para remover nós de texto vazios e combinar adjacentes
            documentoXml.getDocumentElement().normalize();

            System.out.println("Elemento Raiz: " + documentoXml.getDocumentElement().getNodeName());

            // Exemplo de leitura: Obter todos os elementos 'livro'
            NodeList listaLivros = documentoXml.getElementsByTagName("livro");
            System.out.println("--- Detalhes dos Livros ---");
            for (int i = 0; i < listaLivros.getLength(); i++) {
                Element elementoLivro = (Element) listaLivros.item(i);
                String idLivro = elementoLivro.getAttribute("id");
                String titulo = elementoLivro.getElementsByTagName("titulo").item(0).getTextContent();
                String autor = elementoLivro.getElementsByTagName("autor").item(0).getTextContent();
                String ano = elementoLivro.getElementsByTagName("ano").item(0).getTextContent();
                System.out.printf("Livro ID %s: Título=\"%s\", Autor=\"%s\", Ano=%s\n", idLivro, titulo, autor, ano);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Para o exemplo acima, considere um arquivo livros.xml em src/main/resources com o seguinte conteúdo:

<catalogo>
    <livro id="1">
        <titulo>O Senhor dos Anéis</titulo>
        <autor>J.R.R. Tolkien</autor>
        <ano>1954</ano>
    </livro>
    <livro id="2">
        <titulo>A Revolução dos Bichos</titulo>
        <autor>George Orwell</autor>
        <ano>1945</ano>
    </livro>
</catalogo>

Modificação e Escrita DOM com JAXP

Após manipular o objeto Document em memória, as alterações podem ser persistidas de volta para um arquivo XML. Isso é feito usando a API JAXP Transformer. O exemplo a seguir modifica um título existente e adiciona um novo livro ao catálogo.

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileOutputStream;

public class ManipuladorDomEscritaExemplo {
    public static void main(String[] args) {
        try {
            DocumentBuilderFactory fabricaConstrutor = DocumentBuilderFactory.newInstance();
            DocumentBuilder construtorDocumento = fabricaConstrutor.newDocumentBuilder();
            Document documentoXml = construtorDocumento.parse(new File("src/main/resources/livros.xml"));

            // Exemplo de modificação: Mudar o título do primeiro livro encontrado
            NodeList elementosTitulo = documentoXml.getElementsByTagName("titulo");
            if (elementosTitulo.getLength() > 0) {
                elementosTitulo.item(0).setTextContent("O Hobbit - Edição Ilustrada");
                System.out.println("Título do primeiro livro modificado.");
            }

            // Exemplo de adição: Adicionar um novo livro ao catálogo
            Element elementoRaiz = documentoXml.getDocumentElement(); // <catalogo>

            Element novoLivro = documentoXml.createElement("livro");
            novoLivro.setAttribute("id", "3");
            
            Element novoTitulo = documentoXml.createElement("titulo");
            novoTitulo.setTextContent("1984");
            novoLivro.appendChild(novoTitulo);

            Element novoAutor = documentoXml.createElement("autor");
            novoAutor.setTextContent("George Orwell");
            novoLivro.appendChild(novoAutor);

            Element novoAno = documentoXml.createElement("ano");
            novoAno.setTextContent("1949");
            novoLivro.appendChild(novoAno);

            elementoRaiz.appendChild(novoLivro);
            System.out.println("Novo livro '1984' adicionado.");

            // Escrever o documento modificado para um novo arquivo
            TransformerFactory fabricaTransformador = TransformerFactory.newInstance();
            Transformer transformador = fabricaTransformador.newTransformer();
            // Habilitar indentação para legibilidade
            transformador.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
            transformador.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

            DOMSource fonte = new DOMSource(documentoXml);
            StreamResult resultado = new StreamResult(new FileOutputStream("src/main/resources/livros_atualizados.xml"));
            transformador.transform(fonte, resultado);

            System.out.println("Documento XML modificado e salvo em livros_atualizados.xml");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Aálise SAX com JAXP

A análise SAX é orientada a eventos. Para utilizá-la, é necessário criar um manipulador de eventos (ContentHandler) que define a lógica a ser executada quando o parser encontra diferentes partes do documento XML. Este exemplo imprime detalhes básicos de elementos e seus conteúdos.

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;

public class AnalisadorSaxCatalogo {
    public static void main(String[] args) {
        try {
            // 1. Criar uma fábrica de parsers SAX
            SAXParserFactory fabricaParser = SAXParserFactory.newInstance();
            // 2. Obter um parser SAX
            SAXParser parserSax = fabricaParser.newSAXParser();
            // 3. Obter o leitor XML subjacente
            XMLReader leitorXml = parserSax.getXMLReader();

            // 4. Definir o manipulador de conteúdo personalizado
            leitorXml.setContentHandler(new CatalogoHandlerPersonalizado());

            // 5. Iniciar a análise do documento XML
            // Certifique-se de que 'livros.xml' exista em 'src/main/resources/'
            leitorXml.parse("src/main/resources/livros.xml");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class CatalogoHandlerPersonalizado implements ContentHandler {
    private StringBuilder dadosElemento = new StringBuilder();

    @Override
    public void startDocument() throws SAXException {
        System.out.println("--- Início da Análise do Documento XML ---");
    }

    @Override
    public void endDocument() throws SAXException {
        System.out.println("--- Fim da Análise do Documento XML ---");
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
        // Limpa o buffer de dados para o novo elemento
        dadosElemento.setLength(0); 
        System.out.print("Encontrado elemento: <" + qName);
        if (atts != null && atts.getLength() > 0) {
            for (int i = 0; i < atts.getLength(); i++) {
                System.out.print(" " + atts.getQName(i) + "=\"" + atts.getValue(i) + "\"");
            }
        }
        System.out.println(">");
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        String conteudo = dadosElemento.toString().trim();
        if (!conteudo.isEmpty()) {
            System.out.println("  Conteúdo de <" + qName + ">: \"" + conteudo + "\"");
        }
        System.out.println("Fechado elemento: </" + qName + ">");
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        // Coleta os caracteres entre as tags
        dadosElemento.append(new String(ch, start, length));
    }

    // Métodos restantes da interface ContentHandler (geralmente não implementados para casos simples)
    @Override public void setDocumentLocator(Locator locator) {}
    @Override public void startPrefixMapping(String prefix, String uri) {}
    @Override public void endPrefixMapping(String prefix) {}
    @Override public void ignorableWhitespace(char[] ch, int start, int length) {}
    @Override public void processingInstruction(String target, String data) {}
    @Override public void skippedEntity(String name) {}
}

Dom4j

Dom4j é uma biblioteca de código aberto popular e flexível para trabalhar com XML em Java. É conhecida por sua API entuitiva e excelente desempenho, sendo amplamente utilizada, inclusive por frameworks como o Hibernate.

Obtenção e Criação de Doucmentos com Dom4j

Dom4j oferece diversas maneiras de obter um objeto Document, seja lendo de um arquivo, analisando uma string XML ou criando um novo documento do zero. O exemplo demonstra essas três abordagens.

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;

public class Dom4jOperacoesDocumento {
    public static void main(String[] args) {
        Document documentoCarregado = null;
        try {
            // 1. Carregar um documento XML de um arquivo
            SAXReader leitorArquivo = new SAXReader();
            // Usando o mesmo arquivo de exemplo 'livros.xml'
            documentoCarregado = leitorArquivo.read(new File("src/main/resources/livros.xml"));
            System.out.println("1. Documento carregado de arquivo. Elemento raiz: " + documentoCarregado.getRootElement().getName());
            System.out.println("   Primeiro livro (título): " + documentoCarregado.getRootElement().element("livro").elementText("titulo"));

            // 2. Analisar uma string XML para criar um Documento
            String xmlConfiguracao = "<config><app nome='ERP'><versao>1.0</versao></app></config>";
            Document docViaString = DocumentHelper.parseText(xmlConfiguracao);
            System.out.println("\n2. Documento criado a partir de string. Nome do app: " + docViaString.getRootElement().element("app").attributeValue("nome"));

            // 3. Criar um novo documento XML do zero
            Document novoDocVazio = DocumentHelper.createDocument();
            Element raizVazia = novoDocVazio.addElement("configuracoes-sistema");
            Element modulo = raizVazia.addElement("modulo").addAttribute("id", "mod001");
            modulo.addElement("nome").setText("Modulo de Pagamentos");
            modulo.addElement("status").setText("Ativo");
            System.out.println("\n3. Novo documento criado a partir do zero. Raiz: " + novoDocVazio.getRootElement().getName());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Escrita de Documentos com Dom4j

Para persistir um objeto Document Dom4j em um arquivo, utiliza-se a classe XMLWriter, que permite configurar opções de formatação como indentação e codificação.

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import java.io.FileOutputStream;

public class Dom4jGravadorXML {
    public static void main(String[] args) {
        try {
            // Cria um novo documento para demonstração de escrita
            Document documentoParaGravar = DocumentHelper.createDocument();
            Element raizPrincipal = documentoParaGravar.addElement("registros");
            
            Element entrada1 = raizPrincipal.addElement("entrada");
            entrada1.addAttribute("data", "2023-10-26");
            entrada1.addElement("tipo").setText("Compra");
            entrada1.addElement("valor").setText("150.75");
            
            Element entrada2 = raizPrincipal.addElement("entrada");
            entrada2.addAttribute("data", "2023-10-27");
            entrada2.addElement("tipo").setText("Venda");
            entrada2.addElement("valor").setText("300.00");

            // Configura o formato de saída: indentação (pretty print) e codificação
            OutputFormat formatoSaida = OutputFormat.createPrettyPrint(); 
            formatoSaida.setEncoding("UTF-8"); 

            // Escreve o documento para um arquivo no diretório 'src/main/resources/'
            XMLWriter escritor = new XMLWriter(new FileOutputStream("src/main/resources/registros_saida.xml"), formatoSaida);
            escritor.write(documentoParaGravar);
            escritor.close();

            System.out.println("Documento Dom4j salvo com sucesso em registros_saida.xml");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Tags: java XML JAXP DOM SAX

Publicado em 7-20 12:13