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.
GO TO FULL VERSION