Visor XML
Árbol plegable y código fuente, uno al lado del otro.
La estructura del documento aparece aquí en cuanto pegues algo.
Todo se ejecuta en esta pestaña. Nada de lo que pegues se sube, se registra ni se envía a ningún sitio. Abre el panel de red y compruébalo.
Pega un documento arriba y aparece dos veces: el código fuente a la izquierda con resaltado de sintaxis, números de línea y plegado, y un árbol plegable a la derecha construido a partir del mismo análisis. Todos los nodos empiezan desplegados; pulsa un elemento para plegarlo junto con todo lo que hay debajo. Los dos paneles salen de un único escaneo, así que el árbol y la lista de errores describen siempre el mismo documento.
Una vista de árbol se gana su sitio cuando el documento no lo escribiste tú. Una respuesta de API de un proveedor cuya documentación es un PDF, una configuración de 4.000 líneas que dejó un equipo anterior, una exportación que debería tener un registro y puede tener doscientos. La pregunta no suele ser dónde está el error de sintaxis, sino qué forma tiene esto y dónde vive el valor que necesitas.
Valida mientras lees, informando de cada problema de buena formación con línea, columna y una solución sugerida en vez de parar en el primero. Y no se sube nada: el analizador, el árbol y el editor se ejecutan todos en esta pestaña.
Qué muestra el árbol
Cada elemento es una fila: el nombre de la etiqueta tal como está escrito, prefijo incluido, seguido de sus atributos como pares nombre="valor". Un elemento sin hijos se marca como "vacío", así que puedes distinguir <status/> de un nodo que has plegado. El texto aparece como hoja bajo su elemento, truncado a 200 caracteres, porque un árbol es para la estructura y un blob base64 de 40 KB renderizado entero la sepultaría. Los comentarios se muestran como una fila de marcador.
Dos cosas no aparecen deliberadamente: las secciones CDATA y las instrucciones de procesamiento. Para eso lee el panel de código, que muestra el documento exactamente como lo pegaste y es siempre la autoridad. El árbol es DOM real, un nodo plegable por elemento, que es lo que hace que plegar sea instantáneo y el texto seleccionable. También es su coste: un documento con decenas de miles de elementos tarda más en construir el árbol que en analizar el XML.
Para qué sirve un árbol, y en qué es malo
Un árbol responde preguntas de forma más rápido de lo que jamás lo hará el scroll. Pliega todo un nivel por debajo y las secciones de primer nivel caben en una pantalla. Pliega un Header de SOAP y el Body sube al principio del panel. Mira las filas repetidas bajo un contenedor y sabrás si la respuesta lleva un registro o muchos, que es la diferencia entre un consumidor que funciona en pruebas y otro que pierde datos en producción. Es malo en tres cosas, y decirlo es más útil que fingir lo contrario.
- Editar. El árbol se regenera de tu entrada en cada pulsación, así que no hay nada donde escribir. Edita en el panel de código y mira cómo se reconstruye el árbol.
- Comparar. Dos árboles uno al lado del otro son peor manera de encontrar una diferencia que un diff que normaliza el formato de ambos lados primero.
- Extraer. "Todos los precios de todas las líneas" es una expresión XPath, no un ejercicio de scroll.
Anidamiento profundo, y por qué los prefijos se atenúan
El XML con espacios de nombres es difícil de leer por una razón que no tiene que ver con que los espacios de nombres sean complicados. En soap:Body y ns1:PlaceOrder el prefijo es andamiaje: dice a qué vocabulario pertenece el nombre y, una vez lo sabes, es ruido en cada línea siguiente. A ocho niveles de sangría, buena parte de los caracteres en pantalla son prefijos y dos puntos.
Por eso el editor dibuja el prefijo y sus dos puntos con menos opacidad frente al nombre local, y soap:Body se lee como Body con una marca pegada. No se oculta ni se altera nada; el texto se sigue seleccionando y se copia intacto. La excepción es una declaración, donde en xmlns:soap el prefijo es la carga útil y no el andamiaje, así que se queda a plena intensidad. Un prefijo usado sin haber sido vinculado nunca se informa como error, junto con la declaración que hay que añadir, que es el resultado habitual de copiar un fragmento de un documento mayor.
Encontrar un valor
Para una cadena literal, pon el cursor en el panel de código y pulsa Ctrl-F, o Cmd-F en un Mac. El panel de búsqueda del editor se abre arriba con resaltado de coincidencias y un campo de reemplazo, y busca en el código, que contiene el texto completo de cada nodo, incluidas las partes que el árbol trunca.
Para una pregunta estructural, usa XPath. //order[status="failed"]/@id responde en un paso lo que el scroll responde en veinte, y el probador de XPath de este sitio evalúa XPath 1.0 contra tu documento con los prefijos recogidos de él. Un detalle explica la mayoría de los avisos de "mi expresión no devuelve nada": XPath 1.0 no tiene espacio de nombres por defecto. Si la raíz declara xmlns="urn:example:orders", entonces //order no coincide con nada, porque //order significa un elemento order sin espacio de nombres. Vincula un prefijo y escribe //o:order, o recurre a //*[local-name()="order"].
Recorrer un documento desde código
Leer un documento a mano suele ser un paso hacia escribir el código que lo lee todos los días. Estos ejemplos recorren un documento e imprimen su estructura, el equivalente programático del árbol de arriba, y todos analizan de forma segura, porque los valores por defecto de Java y Python resuelven entidades externas.
// Print the shape of a document: element paths with a count, which is
// usually more useful than the tree itself when you are writing a consumer.
function outline(source) {
const doc = new DOMParser().parseFromString(source, 'application/xml');
const error = doc.querySelector('parsererror');
if (error) throw new Error(error.textContent.trim());
const counts = new Map();
const walk = (el, path) => {
const here = path + '/' + el.nodeName;
counts.set(here, (counts.get(here) ?? 0) + 1);
for (const child of el.children) walk(child, here);
};
walk(doc.documentElement, '');
for (const [path, n] of [...counts].sort()) {
console.log(n.toString().padStart(6), path);
}
}
// nodeName keeps the prefix as written. For namespace-aware work use
// el.localName with el.namespaceURI, since the prefix is arbitrary and the
// URI is what identifies the vocabulary.# iterparse reads the document as a stream and lets you drop each element
# once you are done with it, so memory stays flat on files far larger than a
# browser will open.
from defusedxml.ElementTree import iterparse
from collections import Counter
counts = Counter()
stack = []
for event, el in iterparse('document.xml', events=('start', 'end')):
if event == 'start':
stack.append(el.tag)
counts['/'.join(stack)] += 1
else:
stack.pop()
el.clear() # release the subtree; this is the whole point
for path, n in sorted(counts.items()):
print(f'{n:6d} {path}')
# ElementTree reports tags as '{namespace-uri}local' rather than 'prefix:local'
# because the prefix is arbitrary. Split on '}' when you want the local name.import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
public final class Outline {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true); // off by default
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
Document doc = dbf.newDocumentBuilder().parse(new java.io.File(args[0]));
print(doc.getDocumentElement(), 0);
}
static void print(Node node, int depth) {
if (node.getNodeType() != Node.ELEMENT_NODE) return;
Element el = (Element) node;
StringBuilder line = new StringBuilder(" ".repeat(depth));
line.append(el.getNodeName());
NamedNodeMap attrs = el.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Node a = attrs.item(i);
line.append(' ').append(a.getNodeName())
.append("=\"").append(a.getNodeValue()).append('"');
}
System.out.println(line);
NodeList kids = el.getChildNodes();
for (int i = 0; i < kids.getLength(); i++) print(kids.item(i), depth + 1);
}
}using System.Xml;
using System.Xml.Linq;
// XDocument.Load uses a reader with DtdProcessing.Prohibit by default, so
// external entities are never fetched. Construct the reader yourself when
// you want that guarantee stated in the code rather than in the docs.
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null,
MaxCharactersInDocument = 20L * 1024 * 1024,
};
using var reader = XmlReader.Create("document.xml", settings);
var doc = XDocument.Load(reader);
// One line per distinct element path, with how often it occurs.
var paths = doc.Descendants()
.GroupBy(e => string.Join(
"/",
e.AncestorsAndSelf().Reverse().Select(a => a.Name.LocalName)))
.OrderBy(g => g.Key);
foreach (var group in paths)
{
Console.WriteLine(group.Count().ToString().PadLeft(6) + " " + group.Key);
}# xmllint has an interactive tree browser almost nobody knows about. It gives
# you ls, cd, cat, du and pwd over the document, with XPath as the path
# syntax: the terminal equivalent of the tree on this page.
xmllint --nonet --shell document.xml
# / > du show the subtree shape
# / > cd //order[1] navigate by XPath
# / > ls list children of the current node
# / > cat status print a node
# / > exit
# Non-interactive: tag counts, a rough outline of an unfamiliar document.
xmllint --nonet --format document.xml |
grep -o '<[a-zA-Z_][^ />]*' |
sort | uniq -c | sort -rn | head -30
# One value, straight out:
xmllint --nonet --xpath 'string(//order[1]/status)' document.xmlEl esquema con grep del ejemplo de shell es un truco de conteo, no un análisis: contará alegremente un nombre de etiqueta que aparezca dentro de un comentario o de una sección CDATA. Todos los demás ejemplos construyen primero un árbol, que es la diferencia entre una respuesta aproximada y una correcta.
Preguntas frecuentes
¿Se sube mi XML para verlo?
No. El analizador, el árbol y el editor son JavaScript ejecutándose en esta pestaña. No hay componente de servidor, ni analítica con acceso al editor, ni scripts de terceros. Abre la pestaña Red y pega un documento: la página carga sus propios recursos una vez y después se calla.
Esa comprobación importa aquí porque visualizar es justo lo que haces con datos de otras personas. El documento que pegas en un visor suele ser una respuesta de API en vivo o una exportación de producción, con nombres, números de cuenta o un token en una cabecera. Varias herramientas posicionadas para esta búsqueda lo envían a un servidor, y una hace públicos por defecto los documentos guardados.
¿Para qué sirve realmente una vista de árbol?
Para responder preguntas sobre la estructura: qué elementos se repiten, cuánto se anida, en qué rama vive un valor, si un elemento opcional está presente en esta respuesta. Eso es difícil de ver en texto indentado e inmediato en un árbol.
El flujo que se paga solo es: pliega todo un nivel por debajo de la raíz, lee los nombres de las secciones, despliega la que te interesa e ignora las otras 3.000 líneas. Si en cambio la pregunta es de sintaxis, el panel de código y la lista de errores son la mitad de la página que quieres.
¿Puedo editar el XML en el árbol?
No. El árbol se regenera de tu entrada según escribes, así que no hay nada persistente que editar. Haz los cambios en el panel de código de la izquierda.
Es un límite deliberado. Un editor de árbol tiene que decidir qué hacer con el espacio en blanco, el contenido mixto, la colocación de los comentarios y el orden de los atributos en cada cambio, y las respuestas seguras lo vuelven torpe mientras que las cómodas reescriben en silencio partes del documento que no tocaste.
¿Cómo encuentro un valor concreto?
Para una cadena literal, pon el cursor en el panel de código y pulsa Ctrl-F, o Cmd-F en un Mac. El panel de búsqueda se abre arriba con resaltado de coincidencias, y busca en el texto completo en vez de en el texto truncado que muestra el árbol.
Para cualquier cosa estructural, usa XPath: //order[status="failed"]/@id responde en un paso. Si una expresión no devuelve nada y el elemento está claramente ahí, sospecha de un espacio de nombres por defecto, porque XPath 1.0 no tiene ninguno: //order no coincidirá con un elemento en urn:example:orders hasta que vincules un prefijo y escribas //o:order.
¿Por qué mi navegador dice "Esta página contiene los siguientes errores" en vez de mostrar el archivo?
Ese es el visor de XML incorporado de Chrome o Firefox diciéndote que el documento no está bien formado. Los dos paran en el primer error y renderizan el documento solo hasta ahí. El mensaje viene de libxml2 o Expat y está redactado para quien escribió el analizador: "Opening and ending tag mismatch", "Document is empty".
Pega el documento aquí y obtienes todos los problemas a la vez con línea, columna y una sugerencia, además de un árbol para la parte que sí se analizó. Las causas frecuentes son un ampersand suelto, una descarga truncada, una marca de orden de bytes antes de la declaración XML y una página de error HTML servida con un tipo de contenido XML.
¿Qué tamaño de archivo puede abrir?
Hasta 20 MB, y en la práctica el árbol se vuelve pesado antes que el análisis. Escanear un documento de 1 MB lleva unos 110 milisegundos; construir un nodo plegable por elemento lleva más una vez entras en decenas de miles de elementos.
El techo existe porque no se sube nada: el documento y su árbol viven ambos en la memoria de esta pestaña. Para archivos mayores, trabaja en local con algo que procese en flujo, como xmllint --shell para navegar o el iterparse de Python para extraer.