XML (eXtensible Markup Language) é um formato de texto para armazenar e transmitir dados estruturados em forma de árvore. Sobre ele se constroem os feeds de produtos de marketplaces e agregadores, os formatos bancários e de intercâmbio, os arquivos de configuração e também seus formatos derivados: os feeds RSS e os mapas do site. Este artigo faz parte do nosso panorama geral do parsing de documentos, com foco específico em XML.
Como um XML é construído
Um documento XML é uma árvore de elementos. Cada elemento tem um nome (a tag) e pode ter um conjunto de atributos, conteúdo de texto e elementos aninhados.
<catalog>
<product id="101" available="true">
<name>Moedor de café</name>
<price currency="EUR">34.90</price>
</product>
<product id="102" available="false">
<name>Chaleira elétrica</name>
<price currency="EUR">21.90</price>
</product>
</catalog>A diferença-chave em relação ao JSON é que no XML os dados vivem em dois lugares ao mesmo tempo: no texto dos elementos (<name>Moedor de café</name>) e nos atributos (id="101"). É preciso ter isso em mente: na extração, às vezes se consulta o conteúdo da tag e, outras vezes, seus atributos.
Um tema à parte são os espaços de nomes (namespaces). Nos formatos complexos, as tags aparecem como <g:price> ou <atom:link>, em que o prefixo está ligado a um URI. Os parsers exigem levar o namespace em conta na busca de nós; caso contrário, a consulta devolve um resultado vazio — é a causa mais frequente do «o parser não encontra nada, embora os dados estejam lá».
Duas abordagens: árvore em memória e leitura em fluxo
Existem duas maneiras radicalmente distintas de fazer o parsing. A abordagem DOM constrói a árvore inteira em memória e oferece uma navegação cômoda, XPath incluída: é simples e serve para arquivos de até dezenas de megabytes. A abordagem em fluxo (SAX / iterparse) lê o arquivo por eventos, sem carregá-lo por inteiro, e é insubstituível com dumps de centenas de megabytes ou de gigabytes. Para as tarefas típicas, o DOM quase sempre basta; passa-se ao modo em fluxo quando o arquivo deixa de caber na memória.
Python
A biblioteca padrão inclui o módulo xml.etree.ElementTree: é suficiente para os casos simples e não exige instalação.
import xml.etree.ElementTree as ET
tree = ET.parse("catalog.xml")
root = tree.getroot()
for product in root.findall("product"):
name = product.find("name").text
price = product.find("price").text
available = product.get("available") # leitura do atributo
print(name, price, available)Para o trabalho sério, usa-se o lxml: é mais rápido, digere melhor o XML «sujo» e suporta XPath por completo, namespaces incluídos.
from lxml import etree
tree = etree.parse("catalog.xml")
# XPath: todos os produtos disponíveis
for product in tree.xpath("//product[@available='true']"):
name = product.xpath("string(name)")
price = product.xpath("string(price)")
print(name, price)Se os dados chegaram de uma API em XML, mas é mais cômodo trabalhar com eles como com um dicionário, ajuda a biblioteca xmltodict, que converte o XML em estruturas aninhadas comuns de Python — bem parecido com o processamento de um JSON.
JavaScript / Node.js
No Node, duas soluções são populares. O fast-xml-parser parseia o XML para um objeto sem dependências externas e funciona muito rápido.
const { XMLParser } = require("fast-xml-parser");
const fs = require("fs");
const xml = fs.readFileSync("catalog.xml", "utf-8");
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@_" });
const data = parser.parse(xml);
for (const product of data.catalog.product) {
console.log(product.name, product.price["#text"], product["@_available"]);
}A alternativa é o xml2js, veterano no ecossistema e cômodo para o processamento assíncrono. No navegador, está disponível o DOMParser embutido, que permite percorrer o XML com os mesmos métodos de um documento HTML.
PHP
Para os documentos simples, o SimpleXML embutido cai como uma luva: dá acesso aos elementos como se fossem propriedades de um objeto.
<?php
$xml = simplexml_load_file("catalog.xml");
foreach ($xml->product as $product) {
$name = (string) $product->name;
$price = (string) $product->price;
$available = (string) $product["available"]; // atributo
echo "$name — $price — $available\n";
}Quando são necessários namespaces, XPath complexo ou o processamento em fluxo de arquivos grandes, passa-se ao DOMDocument e ao XMLReader, incluídos na mesma distribuição padrão do PHP.
Go
A biblioteca padrão do Go inclui o pacote encoding/xml, que parseia o XML diretamente para structs por meio das tags dos campos.
package main
import (
"encoding/xml"
"fmt"
"os"
)
type Catalog struct {
Products []struct {
ID string `xml:"id,attr"`
Available string `xml:"available,attr"`
Name string `xml:"name"`
Price string `xml:"price"`
} `xml:"product"`
}
func main() {
data, _ := os.ReadFile("catalog.xml")
var c Catalog
xml.Unmarshal(data, &c)
for _, p := range c.Products {
fmt.Println(p.Name, p.Price, p.Available)
}
}Erros típicos
O mais comum é tropeçar na codificação: se a declaração anuncia ISO-8859-1 (ou windows-1252) e o arquivo é lido como UTF-8, os caracteres viram lixo — é preciso deixar a codificação por conta do parser ou recodificar de forma explícita. A segunda causa em frequência são os namespaces ignorados, que fazem as consultas XPath devolverem em silêncio um resultado vazio. A terceira, tentar carregar por inteiro no DOM um arquivo grande demais: um dump de gigabytes deve ser lido em fluxo. E, por fim, não parseie XML com expressões regulares: o formato é aninhado e recursivo, e as regex quebram no primeiro caso fora do comum — para texto sem estrutura elas se justificam (veja o parsing de TXT), mas não para XML.
Se no seu caso há um esquema de exportação fora do padrão, namespaces quebrados ou arquivos de gigabytes, configuramos o parsing de XML sob medida para o seu formato. E se os dados de origem não chegam em XML puro, mas em seus derivados, consulte os materiais vizinhos: RSS e sitemap.