kb.erickguedes.com
XML: Estrutura e Processamento

SAX vs DOM: Parsers XML

Aula 4 de 5

DOM — Document Object Model

DOM carrega todo o documento em memória como uma árvore. Permite navegação bidirecional, modificação e re-consulta.

import javax.xml.parsers.*;
import org.w3c.dom.*;

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

// Navegar na árvore DOM
NodeList produtos = doc.getElementsByTagName("produto");
for (int i = 0; i < produtos.getLength(); i++) {
    Element produto = (Element) produtos.item(i);
    String nome = produto.getElementsByTagName("nome").item(0).getTextContent();
    String preco = produto.getElementsByTagName("preco").item(0).getTextContent();
    System.out.println(nome + ": R$ " + preco);
}

SAX — Simple API for XML

SAX é event-driven: lê o documento sequencialmente e dispara eventos (startElement, characters, endElement).

import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.*;

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();

DefaultHandler handler = new DefaultHandler() {
    boolean bNome = false;

    public void startElement(String uri, String localName,
                             String qName, Attributes attributes) {
        if (qName.equalsIgnoreCase("nome")) bNome = true;
    }

    public void characters(char[] ch, int start, int length) {
        if (bNome) {
            System.out.println("Nome: " + new String(ch, start, length));
            bNome = false;
        }
    }
};

saxParser.parse("catalogo.xml", handler);

StAX — Streaming API for XML

StAX é pull-based: o programador controla quando puxar o próximo evento.

import javax.xml.stream.*;

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(
    new FileInputStream("catalogo.xml"));

while (reader.hasNext()) {
    int event = reader.next();
    if (event == XMLStreamConstants.START_ELEMENT) {
        if (reader.getLocalName().equals("preco")) {
            System.out.println("Preço: " + reader.getElementText());
        }
    }
}
reader.close();

Comparação de Performance

# Testar tempo de parsing DOM vs SAX com arquivo grande
# Gerar arquivo XML de 100MB para teste
python -c "
import xml.etree.ElementTree as ET
root = ET.Element('dados')
for i in range(1000000):
    item = ET.SubElement(root, 'item')
    item.set('id', str(i))
    ET.SubElement(item, 'valor').text = str(i * 1.5)
tree = ET.ElementTree(root)
tree.write('grande.xml', encoding='utf-8', xml_declaration=True)
"

# DOM (consome muita memória)
python -c "
import xml.etree.ElementTree as ET
import time
start = time.time()
tree = ET.parse('grande.xml')
root = tree.getroot()
print(f'DOM: {len(root)} itens em {time.time()-start:.2f}s')
"

# SAX (event-driven, memória constante)
python -c "
import xml.parsers.expat
import time
start = time.time()
count = 0
def start(name, attrs):
    global count
    count += 1
p = xml.parsers.expat.ParserCreate()
p.StartElementHandler = start
with open('grande.xml', 'rb') as f:
    p.Parse(f.read(), True)
print(f'SAX: {count} elementos em {time.time()-start:.2f}s')
"

Lab: SAX vs DOM na Prática

# 1. Documento para teste
cat << 'EOF' > dados.xml
<?xml version="1.0"?>
<biblioteca>
  <livro genero="ficcao">
    <titulo>1984</titulo>
    <autor>George Orwell</autor>
    <ano>1949</ano>
  </livro>
  <livro genero="ficcao">
    <titulo>Duna</titulo>
    <autor>Frank Herbert</autor>
    <ano>1965</ano>
  </livro>
  <livro genero="tecnico">
    <titulo>XML Essencial</titulo>
    <autor>Joao Silva</autor>
    <ano>2023</ano>
  </livro>
</biblioteca>
EOF

# 2. Extrair títulos com XPath (DOM-like)
xmllint --xpath "//livro[ano > 1960]/titulo/text()" dados.xml

# 3. Usar Python SAX para contar livros por gênero
python -c "
import xml.parsers.expat

generos = {}
current = {}
def start(name, attrs):
    global current
    if name == 'livro':
        current['genero'] = attrs.get('genero')
    elif name == 'genero':
        current['tag'] = 'genero'
def end(name):
    global current
    if name == 'livro':
        g = current.get('genero', 'unknown')
        generos[g] = generos.get(g, 0) + 1
    elif name in ('genero',):
        current.pop('tag', None)
def text(data):
    pass

p = xml.parsers.expat.ParserCreate()
p.StartElementHandler = start
p.EndElementHandler = end
p.CharacterDataHandler = text
with open('dados.xml', 'rb') as f:
    p.Parse(f.read(), True)
print('Livros por gênero:', generos)
"

DOM para documentos pequenos e edição; SAX/StAX para grandes volumes e streaming.