Verificador de sintaxe XML
Todos os erros de sintaxe de uma vez, com linha, coluna e correção.
Tudo roda nesta aba. Nada do que você colar é enviado, registrado ou transmitido para lugar nenhum. Abra o painel de rede e confira.
Cole um documento acima, ou solte um arquivo sobre o editor, e ele é verificado contra as regras de boa formação do XML 1.0 enquanto você digita. Cada violação é listada com linha, coluna e o que escrever no lugar, e clicar num resultado leva o cursor até o caractere problemático.
Esta é a verificação que precisa passar antes que qualquer outra possa rodar. Um validador de esquema, um processador XSLT, uma pilha SOAP e um navegador se recusam a olhar um documento com a sintaxe quebrada, então um "premature end of data in tag" saindo do seu build é problema de sintaxe, e ler o XSD não vai explicar isso.
O que é diferente aqui é que o scanner não para na primeira falha. Ele se recupera e segue, então uma única passagem reporta o atributo sem aspas na linha 4, o & solto na linha 12 e a tag deixada aberta no fim. Cada uma das 37 classes de erro tem a própria redação e a própria correção, em vez de um "Syntax Error" para tudo. Nada é enviado: abra o painel de rede e veja que ele continua vazio.
O que "bem formado" realmente significa
O XML 1.0 nomeia doze restrições de boa formação e deixa o resto na gramática. Reduzido a verificações que um documento passa ou não passa, dá esta lista, e cada linha dela é aplicada aqui.
- Exatamente um elemento raiz, sem nada fora dele além de comentários, instruções de processamento e espaço em branco.
- Toda tag de abertura fechada por uma de fechamento cujo nome combine exatamente. Nomes diferenciam maiúsculas, então </Note> não fecha <note>.
- Elementos aninhados, não sobrepostos: <b><i>x</b></i> não é XML, por mais tolerante que o seu analisador de HTML tenha sido.
- Valores de atributo entre aspas, e nenhum atributo repetido no mesmo elemento.
- Nenhum < literal no texto nem em valor de atributo, e nenhum & solto fora de uma referência. Um > é legal no texto; a sequência ]]> não é.
- Apenas as cinco entidades predefinidas — amp, lt, gt, quot e apos — a não ser que uma DTD declare outras. é de HTML e não é uma delas.
- Nenhum caractere fora do conjunto que o XML permite, nenhum comentário contendo --, e uma declaração, se houver, no começo do arquivo e escrita como version, depois encoding, depois standalone.
- Todo prefixo de namespace vinculado por uma declaração xmlns em escopo. Essa regra vem de Namespaces in XML, não do XML 1.0, e é a que mais validadores gratuitos pulam: um concorrente de peso dá <ns:root> como válido sem nenhuma vinculação no arquivo.
Todos os erros de uma vez, e por que outras ferramentas dão um
A especificação diz ao processador que ele não deve continuar o processamento normal depois de um erro fatal, e é por isso que DOMParser, Expat e .NET reportam o primeiro problema e param. Isso é comportamento correto, não preguiça, e também é o motivo de consertar um documento grande custar uma ida e volta por erro.
A recuperação aqui é deliberada. Quando uma tag de fechamento não bate, o scanner procura aquele nome mais acima na pilha de elementos abertos: encontrá-lo significa que o erro real são os elementos deixados abertos no meio, então cada um é reportado na própria linha de abertura em vez de culpar a tag de fechamento. Um caractere estranho dentro de um nome absorve o token inteiro, então amount$="10" gera uma mensagem sobre o cifrão em vez de cinco sobre o sinal de igual e as aspas. Uma ressalva: um erro estrutural logo no começo faz tudo depois dele parecer errado, então conserte os dois ou três primeiros e verifique de novo. O relatório para em 200 problemas, com uma nota dizendo isso.
As falhas que realmente aparecem
Erros de sintaxe reais raramente são exóticos. Eles custam tempo porque o analisador nomeia um sintoma enquanto a causa está em outro lugar, ou é invisível.
- Uma marca de ordem de bytes UTF-8 antes da declaração. Legal, invisível e a causa habitual de "Content is not allowed in prolog". Aqui é reportada como ela mesma, e não como conteúdo misterioso.
- Uma declaração escrita como <?xml encoding="UTF-8" version="1.0"?>, ou uma que diz windows-1252 enquanto os bytes são UTF-8. O segundo caso vira um aviso sobre o que um analisador em nível de bytes vai ler, porque o arquivo já chegou decodificado a esta página.
- , — e cerca de cinquenta outras entidades HTML, nomeadas como HTML e não como entidade indefinida genérica, com a referência numérica a usar no lugar.
- Um aviso do PHP ou um stack trace grudado depois da tag raiz de fechamento, e é por isso que essa mensagem aponta para o corpo HTTP cru.
- Caracteres de controle vindos de uma coluna de banco, que o XML 1.0 proíbe em todo lugar, CDATA incluído.
- Um prefixo soap:, xsi: ou xlink: sem vínculo, depois de um trecho ter sido copiado de um documento maior deixando a declaração xmlns na raiz original.
Onde a verificação de sintaxe para
Esta página responde a uma pergunta: a sintaxe é legal. Ela nunca pergunta se um <invoice> pode conter uma <line>, nem se falta um elemento obrigatório. Isso precisa de um esquema, então mora nos validadores de XSD e de DTD. Se um sistema chamou o seu documento de "inválido", ele rodou uma verificação de esquema, e um resultado verde aqui não vai explicar isso.
Dois limites que valem ser ditos. O subconjunto interno da DTD é lido apenas para declarações de entidade, então declarações de elemento e de lista de atributos ali dentro são atravessadas sem serem aplicadas, e DTDs externas são reportadas mas nunca buscadas. O tamanho tem teto de 20 milhões de caracteres, o aninhamento de 512 níveis e a expansão de entidades de 10 milhões, porque tudo roda nesta aba.
Verificando a sintaxe XML em código
A mesma verificação nas linguagens que mais consomem XML, cada uma na forma segura contra entidades externas e expansão de entidades. Repare em quão poucas reportam mais de um erro: isso é a especificação falando, não a biblioteca.
// DOMParser never throws. Given broken input it returns a document whose
// root is <parsererror>, which is why so much code silently accepts XML
// that no other parser would take.
function checkXml(source) {
const doc = new DOMParser().parseFromString(source, 'application/xml');
const error = doc.querySelector('parsererror');
if (!error) return { wellFormed: true, message: null };
// The text is browser-specific. Firefox includes a line and column,
// Chrome's wording differs, and neither exposes them as fields.
return { wellFormed: false, message: error.textContent.trim() };
}
// Browsers do not resolve external entities, so XXE is not reachable here.
// Internal entity expansion is, so cap the input length before parsing:
// if (source.length > 20_000_000) throw new Error('too large');# lxml is the only common Python parser that keeps going after a syntax
# error and hands back the whole list rather than the first entry.
from lxml import etree
parser = etree.XMLParser(
recover=True, # keep parsing so error_log fills up
resolve_entities=False, # do not expand entities
no_network=True, # never fetch an external DTD
load_dtd=False,
huge_tree=False, # keep libxml2's depth and expansion limits
)
etree.fromstring(source.encode('utf-8'), parser)
for entry in parser.error_log:
print(f"line {entry.line}, column {entry.column}: {entry.message}")
if not parser.error_log:
print('well-formed')
# Without recover=True, fromstring raises etree.XMLSyntaxError and
# e.position gives a (line, column) tuple for the first failure only.
# For untrusted input with the standard library, use defusedxml.import javax.xml.XMLConstants;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// Drop the next line only if your documents legitimately carry a DOCTYPE.
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
var reader = factory.newSAXParser().getXMLReader();
reader.setErrorHandler(new DefaultHandler() {
@Override public void warning(SAXParseException e) { print("warning", e); }
@Override public void error(SAXParseException e) { print("error", e); }
@Override public void fatalError(SAXParseException e) throws SAXParseException {
print("fatal", e);
throw e; // the specification requires processing to stop here
}
private void print(String kind, SAXParseException e) {
System.err.printf("%s at line %d, column %d: %s%n",
kind, e.getLineNumber(), e.getColumnNumber(), e.getMessage());
}
});
reader.parse(new InputSource(new java.io.StringReader(source)));using System.Xml;
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit, // no DTD, so no entity expansion
XmlResolver = null, // never fetch anything
MaxCharactersFromEntities = 1024 * 1024,
MaxCharactersInDocument = 20L * 1024 * 1024,
ConformanceLevel = ConformanceLevel.Document,
};
try
{
using var reader = XmlReader.Create(new StringReader(source), settings);
while (reader.Read()) { } // pull every node; syntax errors surface here
Console.WriteLine("well-formed");
}
catch (XmlException e)
{
// LinePosition is the column, 1-based. Reading stops at this point.
Console.Error.WriteLine(
$"line {e.LineNumber}, column {e.LinePosition}: {e.Message}");
}<?php
// libxml recovers internally, so libxml_get_errors() is the closest a PHP
// script gets to a full list rather than a first failure.
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadXML($source, LIBXML_NONET); // NONET blocks external fetches
$errors = libxml_get_errors();
libxml_clear_errors();
foreach ($errors as $e) {
$kind = $e->level === LIBXML_ERR_FATAL ? 'fatal' : 'error';
fprintf(
STDERR,
"%s at line %d, column %d: %s\n",
$kind,
$e->line,
$e->column,
trim($e->message)
);
}
exit($errors ? 1 : 0);# xmllint ships with libxml2 and is already installed on most machines.
# --nonet stops it fetching a DTD the document points at.
xmllint --noout --nonet document.xml
# --recover keeps parsing after a failure, so you see more than the first.
xmllint --noout --nonet --recover document.xml
# Check a whole tree, one file at a time:
find . -name '*.xml' -print0 | xargs -0 -n1 xmllint --noout --nonet
# Exit status: 0 well-formed, 1 unclassified, 3 well-formedness error,
# 4 validation error against a DTD or schema.Todo exemplo desliga alguma coisa, e isso é o conteúdo, não formalidade. A resolução de entidades externas vem ligada por padrão em Java, e a biblioteca padrão do Python não aplica limite de expansão nenhum, então essas flags são o que separa uma verificação de sintaxe de uma primitiva de leitura de arquivos apontada para o seu próprio servidor.
Perguntas frequentes
Como verifico a sintaxe XML sem instalar nada?
Cole o documento no editor no topo desta página. A verificação roda enquanto você digita, sem botão para apertar e sem conta para criar. Você também pode soltar um arquivo .xml em cima: ele é lido pela File API do navegador e nunca é transmitido.
No terminal, o xmllint faz parte do libxml2 e já vem no macOS e na maioria das distribuições Linux: xmllint --noout --nonet seuarquivo.xml não imprime nada quando a sintaxe está legal. Ele para no primeiro erro, que é a diferença prática.
Meu XML é enviado quando eu verifico aqui?
Não. O scanner é JavaScript rodando nesta aba, num Web Worker. Não existe endpoint para onde mandar um documento, nem analytics ou script de relatório de erros com acesso ao editor.
Isso é verificável, não é promessa. Abra as ferramentas de desenvolvedor, vá para a aba Rede, cole um documento e observe: os recursos desta página carregam uma vez e nada mais acontece. Importa porque os documentos que as pessoas colam em verificadores de sintaxe são envelopes SOAP com tokens, asserções SAML e arquivos de configuração com strings de conexão.
Qual a diferença entre verificador de sintaxe e validador XML?
Os dois são usados como sinônimos, e é daí que vem a confusão. Uma verificação de sintaxe pergunta se o documento obedece às regras do próprio XML: tags fechadas, aninhamento correto, uma raiz, atributos entre aspas, "e comercial" escapado. Ela não precisa de nada além do documento.
Validação no sentido da especificação pergunta se o documento corresponde a um esquema, um XSD ou uma DTD: um segundo arquivo descrevendo quais elementos podem aparecer onde. Ela não roda enquanto a sintaxe não estiver limpa, e por isso um "inválido" vindo de uma pilha de middleware às vezes quer dizer erro de sintaxe e às vezes violação de esquema. Esta página faz a primeira verificação e diz isso; os validadores de XSD e DTD daqui fazem a segunda, também sem enviar nada.
Por que isto reporta cinco erros quando meu editor reporta um?
Porque a especificação XML manda um processador conforme parar depois de um erro fatal, e o analisador por trás do seu editor faz isso. A regra se defende: uma vez que uma tag ficou sem fechar, o analisador realmente não sabe o que o resto do documento queria dizer.
Este scanner se recupera: ele se ressincroniza nas fronteiras das tags, absorve inteiro um token de nome defeituoso e prefere culpar uma tag de abertura não fechada a culpar a de fechamento que a revelou. Trate os erros mais no começo como os confiáveis e verifique de novo, já que uma lista longa costuma ter um único erro estrutural perto do topo gerando quase tudo.
Nomes de tag XML diferenciam maiúsculas de minúsculas?
Completamente. <Note> e <note> são elementos diferentes, e </Note> não fecha <note>. Isso pega quem vem do HTML, onde o analisador converte nomes de tag para minúsculas, e é a causa mais comum de erro de tags que não batem em XML escrito à mão.
Vale também para nomes de atributo e prefixos de namespace: xmlns:Soap e xmlns:soap são prefixos diferentes. Quando a divergência é só de caixa, a mensagem diz isso em vez de reportar um descompasso genérico. Vários resumos muito copiados de "regras de sintaxe XML" afirmam que tags XML não diferenciam maiúsculas; estão errados, e segui-los produz documentos que todo analisador real rejeita.
Qual o maior arquivo que ele consegue verificar?
20 milhões de caracteres, mais ou menos 20 MB de ASCII. Um documento de 10 MB é varrido em pouco mais de um segundo e 1 MB em cerca de 110 milissegundos, num Web Worker, então o editor continua respondendo.
Para algo maior, use um analisador em streaming localmente: xmllint, o iterparse do Python ou qualquer analisador SAX verifica a boa formação sem montar a árvore inteira.