Se você já integrou com uma API XML corporativa e recebeu uma rejeição críptica com "element 'Quantity' is not valid in this context", você já encontrou o XSD — mesmo sem saber o nome. XML Schema Definition (XSD) é a linguagem que define exatamente como um XML válido deve parecer: quais elementos são obrigatórios, quais tipos de dados eles contêm, quantas vezes podem aparecer e quais valores são permitidos. Este guia cobre como escrever schemas, validar contra eles e lidar com os erros que você inevitavelmente encontrará.

O XSD é definido pela especificação W3C XML Schema, publicada pela primeira vez em 2001. A recomendação XML Schema Part 2: Datatypes define o sistema de tipos embutido. É verboso e um pouco arcano de escrever à mão, mas é extremamente preciso — exatamente o que você precisa ao trocar dados entre sistemas que não podem tolerar ambiguidade.

Por que validar XML?

  • Detecte erros de dados cedo. A validação na fronteira da API significa que um campo obrigatório ausente falha imediatamente com um erro claro — não 3 etapas depois, quando a inserção no banco de dados lança uma violação de restrição.
  • Documente o contrato. Um schema XSD é documentação executável. Ao contrário de um documento Word que fica desatualizado, um schema é sempre preciso porque o sistema o impõe.
  • Interoperabilidade. Em integrações B2B — faturamento, gestão de pedidos, saúde — ambas as partes validam contra um schema publicado compartilhado. Se for válido, ambos os lados podem processá-lo.
  • Segurança. A validação rejeita entradas malformadas antes que cheguem à lógica de negócios, reduzindo a superfície de ataque para ataques de injeção XML.

Um Exemplo Completo de Schema XSD

Vamos construir um schema para um catálogo de produtos. Isso cobre os recursos XSD mais usados — tipos simples, tipos complexos, sequências, atributos, cardinalidade e restrições de tipo de dados:

xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <!-- Root element -->
  <xs:element name="catalog">
    <xs:complexType>
      <xs:sequence>
        <!-- One or more product elements -->
        <xs:element name="product" type="ProductType" minOccurs="1" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="version" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>

  <!-- Product complex type -->
  <xs:complexType name="ProductType">
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="description" type="xs:string" minOccurs="0"/>
      <xs:element name="price" type="PriceType"/>
      <xs:element name="stock" type="xs:nonNegativeInteger"/>
      <xs:element name="categories" type="CategoriesType"/>
    </xs:sequence>
    <xs:attribute name="id" type="ProductIdType" use="required"/>
    <xs:attribute name="status" type="ProductStatusType" use="optional" default="active"/>
  </xs:complexType>

  <!-- Price with currency attribute -->
  <xs:complexType name="PriceType">
    <xs:simpleContent>
      <xs:extension base="xs:decimal">
        <xs:attribute name="currency" type="CurrencyCodeType" use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <!-- Categories — zero or more category strings -->
  <xs:complexType name="CategoriesType">
    <xs:sequence>
      <xs:element name="category" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <!-- Product ID: alphanumeric, starts with P, 4-10 chars -->
  <xs:simpleType name="ProductIdType">
    <xs:restriction base="xs:string">
      <xs:pattern value="P[A-Z0-9]{3,9}"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- Allowed product statuses -->
  <xs:simpleType name="ProductStatusType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="active"/>
      <xs:enumeration value="discontinued"/>
      <xs:enumeration value="out_of_stock"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- ISO 4217 currency codes — a subset -->
  <xs:simpleType name="CurrencyCodeType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="USD"/>
      <xs:enumeration value="EUR"/>
      <xs:enumeration value="GBP"/>
      <xs:enumeration value="JPY"/>
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

Muita coisa para absorver. Vamos percorrer os conceitos-chave antes de mostrar a validação em ação.

Conceitos-Chave do XSD Explicados

  • xs:element. Declara um elemento. minOccurs e maxOccurs controlam a cardinalidade. O padrão para ambos é 1. Use maxOccurs="unbounded" para repetição ilimitada.
  • xs:complexType. Um elemento que contém elementos filhos ou tem atributos. Elementos simples (somente texto) usam xs:simpleType ou um tipo embutido XSD diretamente.
  • xs:sequence. Os elementos filhos devem aparecer na ordem definida. A alternativa é xs:all (qualquer ordem, cada um no máximo uma vez) ou xs:choice (exatamente um dos elementos listados).
  • xs:attribute use="required". Torna o atributo obrigatório. use="optional" (padrão) permite que esteja ausente. Adicione default="value" para um padrão quando ausente.
  • xs:restriction. Restringe um tipo base. Facetas comuns: xs:pattern (regex), xs:enumeration (valores permitidos), xs:minInclusive/xs:maxInclusive (limites numéricos), xs:minLength/xs:maxLength (comprimento de string).
  • xs:simpleContent + xs:extension. A forma de adicionar atributos a um elemento que também tem conteúdo de texto — como usado no PriceType acima, onde <price currency="USD">149.99</price> tem tanto texto quanto um atributo.

Tipos de Dados XSD Embutidos que Você Realmente Vai Usar

  • xs:string — qualquer conteúdo de texto
  • xs:integer — números inteiros (positivos, negativos ou zero)
  • xs:nonNegativeInteger — inteiros ≥ 0 (bom para quantidades, contagens)
  • xs:decimal — decimal de precisão arbitrária (bom para preços)
  • xs:booleantrue ou false (também aceita 1 e 0)
  • xs:date — data ISO 8601: 2024-01-15
  • xs:dateTime — datetime ISO 8601: 2024-01-15T09:30:00Z
  • xs:anyURI — uma URI/URL
  • xs:base64Binary — dados binários codificados em Base64

Validando XML Contra um XSD em Python (lxml)

O xml.etree.ElementTree padrão do Python não suporta validação XSD. Você precisa do lxml para isso. Vale a dependência — as mensagens de validação do lxml são detalhadas e apontam para a linha exata que está causando o problema:

bash
pip install lxml
python
from lxml import etree

# Load the schema
with open('catalog.xsd', 'rb') as f:
    schema_doc = etree.parse(f)
schema = etree.XMLSchema(schema_doc)

# Valid XML
valid_xml = """<?xml version="1.0"?>
<catalog version="1.0">
  <product id="P0012" status="active">
    <name>Mechanical Keyboard</name>
    <price currency="USD">189.00</price>
    <stock>42</stock>
    <categories>
      <category>Electronics</category>
      <category>Peripherals</category>
    </categories>
  </product>
</catalog>"""

xml_doc = etree.fromstring(valid_xml.encode())
if schema.validate(xml_doc):
    print("Valid!")
else:
    for error in schema.error_log:
        print(f"Error at line {error.line}: {error.message}")

# Invalid XML — missing required 'stock', bad product ID format
invalid_xml = """<?xml version="1.0"?>
<catalog version="1.0">
  <product id="BADID">
    <name>Test Product</name>
    <price currency="USD">10.00</price>
    <categories/>
  </product>
</catalog>"""

invalid_doc = etree.fromstring(invalid_xml.encode())
schema.validate(invalid_doc)
for error in schema.error_log:
    print(f"Line {error.line}: {error.message}")
# Line 3: Element 'product', attribute 'id': 'BADID' is not a valid value of the atomic type 'ProductIdType'.
# Line 7: Element 'categories': Missing child element(s). Expected is ( category ).  ← if minOccurs > 0

Validando em Java — JAXP

Java tem validação XSD embutida via API de validação JAXP (sem necessidade de biblioteca de terceiros). Este é o padrão usado em aplicações Java corporativas e processamento XML com Spring Boot:

java
import javax.xml.XMLConstants;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import org.xml.sax.SAXException;
import java.io.IOException;

SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File("catalog.xsd"));
Validator validator = schema.newValidator();

try {
    validator.validate(new StreamSource(new File("catalog.xml")));
    System.out.println("Valid!");
} catch (SAXException e) {
    System.out.println("Validation error: " + e.getMessage());
} catch (IOException e) {
    System.out.println("IO error: " + e.getMessage());
}

XML Schema vs JSON Schema

Se você já usou JSON Schema, o XSD terá um propósito familiar, mas sintaxe muito diferente. Uma comparação rápida:

  • Sintaxe. XSD é XML; JSON Schema é JSON. O XSD é mais verboso, mas se integra naturalmente em cadeias de ferramentas XML.
  • Maturidade. XSD 1.0 existe desde 2001 — décadas de ferramentas, validadores e suporte de bibliotecas. JSON Schema vem evoluindo desde 2009 e só alcançou um rascunho estável nos últimos anos.
  • Sistema de tipos. O XSD tem um sistema de tipos embutido mais rico: xs:date, xs:decimal, xs:anyURI são embutidos. JSON Schema depende de anotações de formato para datas e URIs, que os validadores podem ou não impor.
  • Suporte a namespaces. O XSD tem suporte nativo a namespaces XML. JSON Schema não tem conceito equivalente.
  • Quando usar qual. Se você está trabalhando em um ecossistema XML, use XSD. Se está em um ecossistema JSON, use JSON Schema. Não misture os dois.

Erros de Validação Comuns e Como Corrigi-los

  • "Element X is not expected." O elemento aparece fora de ordem em um xs:sequence, ou não está definido no schema. Verifique o nome do elemento e sua posição em relação aos irmãos.
  • "The value Y of attribute Z is not valid." O valor do atributo não corresponde ao seu tipo ou enumeração. Comum em campos de status e códigos de moeda com erros de digitação.
  • "Missing child element(s)." Um elemento filho obrigatório (minOccurs > 0) está ausente. Verifique no schema quais elementos são obrigatórios sob o pai.
  • "Not a valid value of the atomic type." Uma restrição de tipo simples falhou — padrão, intervalo ou enumeração. Verifique o schema para a xs:restriction nesse tipo.
  • "Content model is not determinist." Schema ambíguo — o validador não consegue determinar qual ramo se aplica. Geralmente causado por duas opções com o mesmo nome de tag em um xs:choice.

Ferramentas Relacionadas

Trabalhando com schemas XML? Essas ferramentas vão ajudar: Validador XML para verificações rápidas de boa formação, Formatador XML para tornar XML denso legível antes de depurar, Gerador de Schema XML para gerar automaticamente um XSD a partir de um documento XML de exemplo, e XML para JSON quando preferir trabalhar com JSON Schema.

Conclusão

O XSD é verboso, mas é a ferramenta certa para definir contratos XML rígidos em contextos corporativos e B2B. Os padrões-chave para lembrar: use xs:sequence com minOccurs/maxOccurs para controlar a estrutura, use xs:restriction com xs:enumeration e xs:pattern para restrições de valor, e use lxml em Python para validação com mensagens de erro claras. Uma vez que você tenha um schema funcionando, ele se torna a única fonte da verdade que ambos os lados de uma integração podem referenciar — o que economiza muito tempo de depuração de ida e volta.