W3docs

Java XML DOM Parser

Aprenda a analisar, navegar, modificar e serializar XML em Java com o DOM parser integrado, com exemplo completo de leitura e escrita.

O parser DOM (Document Object Model) lê um documento XML inteiro na memória e fornece uma árvore de nós que você pode navegar, consultar e modificar. Ele é fornecido com o JDK em javax.xml.parsers e org.w3c.dom, portanto não é necessário adicionar nada ao classpath. O DOM é a ferramenta certa quando os documentos são pequenos o suficiente para caber na memória e você precisa de acesso aleatório a qualquer parte da árvore — lendo um arquivo de configuração, transformando um payload ou construindo XML programaticamente.

Este capítulo percorre o ciclo de vida completo: como o DOM modela um documento, como analisar uma fonte em uma árvore, como ler e modificar nós e como serializar a árvore de volta como XML. Se você é novo em XML no Java, comece com a introdução ao XML; para documentos grandes onde a memória importa, compare o DOM com o parser SAX de streaming.

Como o DOM modela um documento

O DOM transforma a marcação em uma árvore de objetos Node. Cada elemento, atributo, trecho de texto e comentário é um nó, e o documento inteiro está pendurado em uma única raiz Document. Você lê a árvore pedindo a nós os seus filhos, e a altera criando, movendo ou removendo nós.

ConceitoInterfaceO que representa
DocumentDocumentO arquivo analisado completo; ponto de entrada da árvore
ElementElementUma tag como <book>, com atributos e filhos
AttributeAttrUm par nome/valor em um elemento
TextTextDados de caracteres dentro de um elemento
Node listNodeListUma coleção ordenada e acessível por índice de nós

O principal compromisso: o DOM é conveniente porque toda a árvore é acessível, mas carrega tudo na memória de uma só vez. Para feeds de vários gigabytes, você usaria SAX ou StAX, que transmitem o documento sem construir uma árvore. E se você estiver mapeando XML de e para objetos Java em vez de percorrer nós brutos, o JAXB geralmente requer menos código do que DOM escrito à mão.

Analisando um documento

Você nunca constrói um parser diretamente. Você pede a um DocumentBuilderFactory um DocumentBuilder, depois chama parse em um stream, arquivo ou URI. Configure a factory antes de construir — a consciência de namespace e a validação são configurações em nível de factory.

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);

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

// Collapse adjacent text nodes and drop empty ones so getTextContent is clean.
doc.getDocumentElement().normalize();

parse lança SAXException para XML malformado e IOException se a fonte não puder ser lida, portanto ambas são exceções verificadas que você deve tratar. Chamar normalize() uma vez após a análise mescla nós de texto divididos — uma fonte comum de surpresas ao ler texto de elementos.

Dois métodos cobrem a maior parte da leitura: getElementsByTagName encontra todos os descendentes com uma determinada tag, e getChildNodes retorna os filhos diretos de um nó. Lembre-se de que getChildNodes inclui nós de texto de espaço em branco, então filtre pelo tipo de nó quando quiser apenas elementos.

Element root = doc.getDocumentElement();          // <library>
NodeList books = doc.getElementsByTagName("book"); // every <book> in the tree

for (int i = 0; i < books.getLength(); i++) {
    Element book = (Element) books.item(i);
    String id = book.getAttribute("id");           // attribute by name
    String title = book.getElementsByTagName("title")
                       .item(0).getTextContent();   // first child <title> text
    System.out.println(id + " -> " + title);
}

NodeList é baseado em índice, não iterável, então você percorre com getLength() e item(i). getAttribute retorna uma string vazia (nunca null) quando o atributo está ausente, o que vale saber antes de escrever uma verificação de null que nunca dispara.

Modificando e criando nós

A árvore DOM é mutável. Você altera texto com setTextContent, altera atributos com setAttribute e aumenta a árvore criando nós através dos métodos factory do Document e os anexando. Os nós devem ser criados pelo mesmo documento em que são inseridos.

// Update existing content.
Element price = (Element) book.getElementsByTagName("price").item(0);
price.setTextContent("49.50");
price.setAttribute("currency", "USD");

// Build a new subtree and attach it.
Element added = doc.createElement("book");
added.setAttribute("id", "b3");
Element title = doc.createElement("title");
title.setTextContent("The Pragmatic Programmer");
added.appendChild(title);
doc.getDocumentElement().appendChild(added);

createElement cria um nó desanexado; nada aparece no documento até que você o anexe com appendChild em algum lugar. Para remover um nó, chame parent.removeChild(child).

Serializando a árvore de volta

O DOM não tem toString() que produz XML. Para serializar, entregue o documento a um Transformer com um DOMSource e um StreamResult. O mesmo pacote javax.xml.transform permite escrever em um arquivo, uma string ou qualquer stream, e definir opções de formatação.

Transformer tr = TransformerFactory.newInstance().newTransformer();
tr.setOutputProperty(OutputKeys.INDENT, "yes");
tr.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");

tr.transform(new DOMSource(doc), new StreamResult(new File("out.xml")));

Para entrada não confiável, proteja a factory antes de analisar — desative declarações DOCTYPE com factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) para bloquear ataques XXE (XML External Entity).

Um exemplo completo

Este programa analisa um documento de biblioteca em memória, lê cada livro, aumenta todos os preços em 10%, insere um novo livro e serializa a primeira linha de preço atualizada de volta para XML — exercitando o ciclo completo de leitura-modificação-escrita em uma única árvore.

java— editable, runs on the server

O que extrair da execução:

  • O elemento raiz é impresso como library porque getDocumentElement() retorna o único nó do topo ao qual todo o resto está ligado.
  • getElementsByTagName("book") relata uma contagem de 2 antes da inserção, confirmando que coletou ambos os descendentes <book> da raiz.
  • Os preços são lidos com getTextContent() e analisados com Double.parseDouble, então 45.00 e 38.50 somam o total impresso de 83.50.
  • Após appendChild, a mesma consulta getElementsByTagName("book") retorna 3, mostrando que a árvore em tempo real capturou o nó criado com doc.createElement.
  • A primeira linha de preço serializada lê 49.50, provando que setTextContent mutou o nó em memória e o Transformer escreveu o valor atualizado (45.00 aumentado em 10%) de volta para XML.

Prática

Prática
Na API DOM, por que você deve chamar doc.createElement() antes de appendChild() para adicionar um novo nó?
Na API DOM, por que você deve chamar doc.createElement() antes de appendChild() para adicionar um novo nó?
Was this page helpful?