CodeGym /Cursos /JAVA 25 SELF /Trabalhando com DOM e SAX: parsing de XML

Trabalhando com DOM e SAX: parsing de XML

JAVA 25 SELF
Nível 47 , Lição 1
Disponível

1. DOM (Document Object Model)

Às vezes um arquivo XML é grande demais para carregá-lo inteiro na memória. Também pode acontecer de a estrutura do documento ser desconhecida de antemão ou muito complexa. Em outros casos, é preciso processar apenas partes específicas dos dados, e não o arquivo inteiro, ou até alterar o documento em tempo de execução: remover um nó, adicionar um novo elemento ou reestruturar parte da árvore.

Nessas situações, as ferramentas clássicas e comprovadas — DOM e SAX — são apropriadas. Elas permitem trabalhar diretamente com o conteúdo XML sem depender de classes Java previamente definidas.

Como o DOM funciona

DOM é uma forma de representar um documento XML como uma árvore de objetos em memória. Cada tag se torna um nó da árvore (Node), e atributos, valores de texto e até comentários viram objetos separados. Após carregar o documento, você tem acesso completo à sua estrutura: pode ler, alterar, remover e adicionar elementos e atributos.

Principais classes do DOM em Java

  • DocumentBuilderFactory — fábrica para criar o parser.
  • DocumentBuilder — parser que transforma XML em árvore.
  • Document — objeto raiz da árvore.
  • Element — elemento XML (tag).
  • NodeList — lista de nós (por exemplo, todos os <item> dentro de <items>).

Exemplo: lendo um arquivo XML com DOM

Suponha que precisamos ler um arquivo XML assim:

<contacts>
    <person id="1">
        <name>Ivan</name>
        <email>ivan@example.com</email>
    </person>
    <person id="2">
        <name>Maria</name>
        <email>maria@example.com</email>
    </person>
</contacts>

Código: leitura e iteração de elementos

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.File;

public class DomExample {
    public static void main(String[] args) throws Exception {
        // 1. Criamos a fábrica e o parser
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 2. Carregamos o arquivo XML na memória
        Document doc = builder.parse(new File("contacts.xml"));

        // 3. Obtemos o elemento raiz
        Element root = doc.getDocumentElement();
        System.out.println("Elemento raiz: " + root.getTagName());

        // 4. Obtemos a lista de todos os <person>
        NodeList persons = root.getElementsByTagName("person");

        for (int i = 0; i < persons.getLength(); i++) {
            Element person = (Element) persons.item(i);

            String id = person.getAttribute("id");
            String name = person.getElementsByTagName("name").item(0).getTextContent();
            String email = person.getElementsByTagName("email").item(0).getTextContent();

            System.out.println("id: " + id + ", name: " + name + ", email: " + email);
        }
    }
}

O que está acontecendo aqui:

  • Primeiro criamos o parser e carregamos o arquivo XML.
  • Obtemos o elemento raiz (contacts).
  • Encontramos todos os elementos <person> e iteramos por eles.
  • Para cada <person>, lemos o atributo id e os elementos <name> e <email>.

Esquema da árvore DOM do exemplo

contacts
├── person (id="1")
│   ├── name ("Ivan")
│   └── email ("ivan@example.com")
└── person (id="2")
    ├── name ("Maria")
    └── email ("maria@example.com")

Alterando XML com DOM

DOM permite não apenas ler, mas também alterar XML. Por exemplo, vamos adicionar uma nova pessoa:

// Criamos um novo elemento <person>
Element newPerson = doc.createElement("person");
newPerson.setAttribute("id", "3");

// <name>
Element name = doc.createElement("name");
name.setTextContent("Sergey");
newPerson.appendChild(name);

// <email>
Element email = doc.createElement("email");
email.setTextContent("sergey@example.com");
newPerson.appendChild(email);

// Adicionamos ao nó raiz
root.appendChild(newPerson);

// Salvamos as alterações no arquivo
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(new File("contacts-updated.xml")));

Principais prós e contras do DOM

  • Prós: conveniente para arquivos pequenos, fácil fazer qualquer alteração, é possível navegar livremente pela árvore em qualquer direção.
  • Contras: todo o XML fica na memória. Para arquivos grandes (centenas de MB ou mais) — não é uma boa opção, a memória acaba rapidamente.

2. SAX (Simple API for XML)

SAX é um parser baseado em eventos. Ele não monta a árvore; apenas lê o XML “da esquerda para a direita” e chama handlers de eventos: “início de elemento”, “conteúdo de texto”, “fim de elemento” etc. Você escreve seu próprio handler e reage aos eventos de que precisa.

Analogia:
Se DOM é como espalhar todas as páginas de uma agenda sobre a mesa, SAX é como ler a agenda página a página e fazer anotações apenas quando encontrar a página de interesse.

Principais classes do SAX em Java

  • SAXParserFactory, SAXParser — fábrica e parser.
  • DefaultHandler — classe base para tratar eventos.
  • Métodos de callback: startElement, characters, endElement, startDocument, endDocument.

Exemplo: lendo um arquivo XML com SAX

Suponha o mesmo arquivo contacts.xml. Queremos apenas imprimir os nomes e e-mails de todas as pessoas.

Código: parser SAX

import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;

public class SaxExample {
    public static void main(String[] args) throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        parser.parse(new File("contacts.xml"), new ContactHandler());
    }
}

class ContactHandler extends DefaultHandler {
    private String currentElement = "";
    private String name = "";
    private String email = "";

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        currentElement = qName;
        if ("person".equals(qName)) {
            String id = attributes.getValue("id");
            System.out.println("Nova pessoa, id: " + id);
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        String text = new String(ch, start, length).trim();
        if (text.isEmpty()) return;
        if ("name".equals(currentElement)) {
            name = text;
        } else if ("email".equals(currentElement)) {
            email = text;
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
        if ("person".equals(qName)) {
            System.out.println("Nome: " + name + ", e-mail: " + email);
            name = "";
            email = "";
        }
        currentElement = "";
    }
}

Agora vamos entender, em palavras simples, o que acontece aqui. Quando o parser SAX encontra uma tag de abertura, por exemplo <person>, ele chama o método startElement. Nele, lemos o atributo id e o imprimimos na tela. Quando aparece texto dentro — por exemplo, nome ou e-mail — a execução vai para o método characters, onde salvamos esse texto em variáveis temporárias. E quando o parser chega à tag de fechamento </person>, é chamado endElement. Nesse momento, já sabemos o nome e o e-mail da pessoa e podemos imprimi-los. Depois disso, as variáveis são limpas para ficarem prontas para o próximo contato.

A ideia é que o SAX não mantém todo o XML na memória; ele trabalha como um “leitor” em streaming: percorre o arquivo de cima para baixo e reage a eventos — início de tag, texto, fim de tag. Isso é rápido e econômico em memória, especialmente para arquivos grandes.

Comparação breve entre DOM e SAX

DOM SAX
Estilo Árvore (toda a estrutura na memória) Eventos (processamento em fluxo)
Memória Carrega o XML inteiro Uso mínimo de memória
Alterações Pode ler/alterar/adicionar Somente leitura (geralmente)
Busca de dados Fácil buscar em qualquer lugar É preciso acompanhar “onde você está”
Tamanho do arquivo Para arquivos pequenos e médios Para arquivos muito grandes

3. Quando usar DOM e quando usar SAX

DOM é indicado quando:

  • O arquivo é pequeno ou médio.
  • É preciso acessar repetidamente diferentes partes do XML.
  • É necessário alterar a estrutura do documento.

SAX é a sua escolha quando:

  • O arquivo é enorme e não pode ser carregado na memória.
  • Você precisa extrair rapidamente apenas parte da informação (por exemplo, encontrar todos os elementos <item> com um atributo específico).
  • É necessária alta performance e uso mínimo de memória.
  • Você não pretende alterar o XML, apenas ler.

Dica:
Em projetos reais, costuma-se usar ambos: DOM — para configurações “humanas” e arquivos de configuração pequenos, e SAX — para processar logs, exportações e imports grandes.

4. Tarefa prática: um pequeno parser para o aplicativo

No seu aplicativo de estudo (por exemplo, uma “agenda de contatos”), surgiu a tarefa: contar rapidamente a quantidade de usuários com e-mail no gmail.com.

Solução com DOM:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("contacts.xml"));

NodeList emails = doc.getElementsByTagName("email");
int count = 0;
for (int i = 0; i < emails.getLength(); i++) {
    String email = emails.item(i).getTextContent();
    if (email.endsWith("@gmail.com")) {
        count++;
    }
}
System.out.println("Usuários com gmail.com: " + count);

Solução com SAX:

class GmailCounterHandler extends DefaultHandler {
    private String currentElement = "";
    int count = 0;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        currentElement = qName;
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        if ("email".equals(currentElement)) {
            String email = new String(ch, start, length).trim();
            if (email.endsWith("@gmail.com")) {
                count++;
            }
        }
    }
    @Override
    public void endDocument() {
        System.out.println("Usuários com gmail.com: " + count);
    }
}

5. Particularidades e nuances ao trabalhar com DOM e SAX

  • DOM pode consumir toda a memória se o arquivo XML for muito grande. Se você vir um OutOfMemoryError, provavelmente é hora de migrar para SAX.
  • SAX exige atenção: é preciso acompanhar em qual elemento você está e montar cuidadosamente os dados necessários. Às vezes é necessário usar uma pilha (stack) ou variáveis auxiliares para não se perder em estruturas profundamente aninhadas.
  • No SAX, o método characters pode ser chamado várias vezes para o mesmo bloco de texto (especialmente se o texto for longo ou contiver caracteres especiais). É melhor acumular o texto em um StringBuilder.
  • DOM é ótimo para busca, navegação e alteração da estrutura, mas não para processamento em streaming.
  • Se você não tem certeza de qual abordagem escolher, comece com DOM pela simplicidade; se ficar “pesado” em memória, migre para SAX.

6. Erros típicos ao trabalhar com DOM e SAX

Erro nº 1: Tratamento incorreto de espaços e quebras de linha no SAX.
O método characters pode retornar pedaços de texto, incluindo espaços e quebras de linha entre elementos. Se não filtrar com .trim().isEmpty(), você pode receber muitas chamadas “vazias” ou montar o texto de forma incorreta.

Erro nº 2: Tentar modificar XML com SAX.
SAX é somente para leitura! Se precisar alterar a estrutura, use DOM.

Erro nº 3: Quebrar a ordem dos eventos no SAX.
Se as variáveis não forem zeradas em endElement, pode ocorrer “vazamento” de dados entre elementos.

Erro nº 4: Usar DOM para arquivos enormes.
Resultado — OutOfMemoryError ou desempenho muito lento.

Erro nº 5: Conversão de tipos incorreta no DOM.
No DOM tudo é Node, mas para trabalhar com atributos e elementos filhos é preciso fazer cast para Element. Um cast incorreto pode levar a ClassCastException.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION