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();
}
}
}