Convertidor de XML a YAML
Convierte a YAML, con los valores peligrosos entrecomillados.
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 XML arriba y el YAML aparece al lado. El documento se comprueba en cuanto a buena formación, se mapea a un árbol, y luego lo escribe un emisor cuyo trabajo principal es decidir qué valores hay que entrecomillar. Nada se sube: el escáner, el mapeador y el emisor corren todos en esta pestaña.
La razón habitual para querer esto es que un archivo de configuración, un manifiesto de Kubernetes, una canalización de CI o un inventario de Ansible necesita datos que ahora viven en XML. La salida va a un archivo que una máquina lee literalmente, y por eso el entrecomillado importa más que la maquetación.
YAML parece el formato simpático y es el que con más probabilidad cambiará tus datos en silencio. Un código de país NO sin entrecomillar se convierte en el booleano false en la mayor parte del ecosistema. Un código postal 01730 se convierte en 1730. Una versión 1.10 se convierte en 1.1. Este emisor entrecomilla los valores que de otro modo se leerían mal, y esta página dice exactamente cuáles y por qué.
El mapeo es el mapeo de XML a JSON
YAML 1.2 se diseñó como un superconjunto de JSON, así que aquí no hay un árbol aparte. El XML se convierte a la misma estructura que produce la página de XML a JSON y un serializador distinto lo escribe. Cada decisión de mapeo de esa página se aplica sin cambios: los atributos se convierten en claves con prefijo, el texto que comparte elemento con atributos o hijos va bajo una clave de texto, un elemento que aparece dos veces se convierte en una secuencia, y los comentarios se descartan.
Una cosa empeora. En JSON un consumidor al menos ve corchetes; en YAML la diferencia entre un elemento y dos es un escalar indentado frente a una lista de guiones, y eso nadie lo ve en un diff. Usa el campo «siempre un array» para cualquier cosa que conceptualmente sea una lista, de modo que un documento de un elemento y uno de cincuenta produzcan la misma forma.
<order id="00042">
<total currency="GBP">19.90</total>
<line sku="0071">Widget</line>
<line sku="0072">Gasket</line>
<country>NO</country>
</order>
order:
attr_id: '00042'
total:
attr_currency: GBP
text: '19.90'
line:
- attr_sku: '0071'
text: Widget
- attr_sku: '0072'
text: Gasket
country: 'NO'El problema de Noruega, y la lista exacta que cubre
YAML 1.1 define su tipo booleano por enumeración, y la enumeración es más ancha de lo que cualquiera espera. La página de tipos publicada lista, literalmente: y, Y, yes, Yes, YES, n, N, no, No, NO, true, True, TRUE, false, False, FALSE, on, On, ON, off, Off, OFF. Todos y cada uno de esos, sin entrecomillar, se cargan como booleano.
La consecuencia tiene nombre. Un conjunto de datos de códigos de país ISO recibe NO para Noruega y el analizador le entrega a la aplicación false. La misma lista se traga una columna Sí/No exportada de una hoja de cálculo y cualquier interruptor escrito on u off que pretendía ser texto. YAML 1.2 estrechó el esquema central a solo true y false, pero PyYAML, Psych de Ruby, Ansible y buena parte de las herramientas de Kubernetes siguen resolviendo el conjunto de 1.1, así que da por vivo todo él.
El emisor pone comillas simples a cualquier escalar que coincida exactamente con esa lista, incluidas las formas de una sola letra, más null, Null, NULL y la virgulilla. Fíjate en la distinción de mayúsculas: yES y nO no están en la lista de 1.1 y no se entrecomillan, porque ningún analizador conforme los lee tampoco como booleanos.
Qué más se entrecomilla, y qué se escapa
El conjunto booleano es el caso famoso, no el común. La mayoría de los valores que se rompen son números que nunca fueron números, porque YAML infiere un tipo de la escritura de un escalar plano exactamente como JSON no hace. Un escalar recibe comillas simples cuando coincide con el conjunto booleano o nulo, cuando coincide con una gramática de número de JSON (lo que cubre 42, 19.90 y 1.10), cuando tiene un cero inicial seguido de más dígitos, cuando está vacío, cuando empieza por un carácter indicador de YAML como un guion o una almohadilla, o cuando tiene espacio en blanco en cualquiera de los extremos.
El texto de varias líneas no se entrecomilla. Se convierte en un escalar de bloque literal introducido por una barra vertical con indicador de recorte. Se elige literal sobre plegado a propósito: un bloque plegado refluye los saltos de línea sencillos convirtiéndolos en espacios, destruyendo código y direcciones incrustados. El indicador de recorte quita el salto de línea final que un bloque añadiría de otro modo.
Algunos valores salen del emisor sin comillas y pueden cambiar de tipo aguas abajo. Se listan en lugar de pasarlos por alto, porque ningún emisor que use escalares planos ha resuelto la inferencia de tipos de YAML:
- Números sexagesimales. YAML 1.1 lee 22:22 como un entero en base 60, así que una duración se convierte en 1342 en PyYAML. Un analizador de 1.2 como js-yaml devuelve la cadena, así que esto depende de qué lado lea el archivo.
- Escrituras hexadecimales. 0x1F se carga como 31 tanto en YAML 1.1 como en el esquema central de 1.2, así que un código de color hexadecimal necesita comillas.
- Fechas. 2024-01-05 coincide con el tipo timestamp de YAML, así que js-yaml y PyYAML te entregan los dos un objeto de fecha en lugar de una cadena.
- Escalares de bloque cuya primera línea está indentada más que las líneas de después, lo que pasa cuando una sección CDATA conserva los espacios iniciales. El arreglo de YAML es un indicador de indentación explícito tras la barra vertical, que este emisor no escribe.
Pon el prefijo de atributos y la clave de texto antes de convertir
Esta es la única preparación que merece la pena hacer. Los valores por defecto se eligieron para JSON, donde son seguros, y YAML tiene una gramática más estricta para las claves que para los valores.
El prefijo de atributos por defecto es @_ y la clave de texto por defecto es #text. En YAML, @ es un indicador reservado con el que un escalar plano no puede empezar, así que una clave @_id hace que el documento no se analice: js-yaml informa de «bad indentation of a mapping entry» y PyYAML informa de un carácter que no puede iniciar ningún token. Una # inicial es peor porque no falla. Una línea que dice #text: 19.90 es un comentario, así que el archivo carga y el valor simplemente no está ahí.
Los dos campos están en la fila de controles encima del editor. Pon el prefijo en algo llano como attr_ y la clave de texto en text, y cada clave de la salida será un nombre YAML corriente. Las claves que necesitan comillas por otras razones, como soap:Body, se entrecomillan automáticamente, porque los dos puntos no son legales en una clave desnuda.
Hacer esto desde código
Dos pasos: analizar el XML de forma segura, y luego serializar con un volcador al que hayas dicho que entrecomille como es debido. La mitad del XML necesita las banderas de entidades de siempre, porque los valores por defecto en Java y .NET resolverán un DOCTYPE. La mitad del YAML necesita atención porque los volcadores difieren en lo agresivamente que entrecomillan.
import { XMLParser } from 'fast-xml-parser';
import yaml from 'js-yaml';
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: 'attr_', // not @_: YAML reserves a leading @
textNodeName: 'text', // not #text: a leading # is a comment
parseTagValue: false, // keep values as strings
parseAttributeValue: false,
processEntities: false, // do not expand DOCTYPE-declared entities
isArray: (name) => ['line', 'item', 'entry'].includes(name),
});
const out = yaml.dump(parser.parse(xmlSource), {
lineWidth: -1, // never fold long lines; folding rewrites your data
noRefs: true, // never emit anchors and aliases
quotingType: "'",
sortKeys: false,
});
// js-yaml's dumper is conservative: it quotes NO, 01730, 1.10, 22:22 and
// 0x1F on its own, and quotes keys that begin with @ or #. Add
// forceQuotes: true if you want every string quoted regardless.import xmltodict
import yaml
doc = xmltodict.parse(
xml_source,
disable_entities=True, # blocks the expat entity attacks
attr_prefix='attr_',
cdata_key='text',
force_list=('line', 'item', 'entry'),
)
print(yaml.safe_dump(
doc,
default_flow_style=False,
allow_unicode=True,
sort_keys=False,
width=10 ** 9, # effectively disable line folding
))
# PyYAML implements the YAML 1.1 resolver, so its dumper knows that NO,
# 01730 and 1.10 would load back as a bool, an int and a float, and quotes
# them. Use safe_dump, never dump: the full dumper emits Python-specific
# tags that only yaml.unsafe_load can read back.import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.xml.XmlFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import javax.xml.stream.XMLInputFactory;
XMLInputFactory input = XMLInputFactory.newFactory();
input.setProperty(XMLInputFactory.SUPPORT_DTD, false);
input.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
JsonNode tree = new XmlMapper(new XmlFactory(input)).readTree(xmlSource);
YAMLMapper yaml = YAMLMapper.builder()
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
.disable(YAMLGenerator.Feature.MINIMIZE_QUOTES) // off is the safe state
.enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)
.build();
String out = yaml.writeValueAsString(tree);
// MINIMIZE_QUOTES is the setting to leave alone. It is off by default, and
// turning it on is how a value of NO ends up unquoted in a Jackson-generated
// file that a Python service then reads as false.using System.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null,
MaxCharactersFromEntities = 1024 * 1024,
};
using var reader = XmlReader.Create(new StringReader(xmlSource), settings);
var document = new XmlDocument { XmlResolver = null };
document.Load(reader);
string json = JsonConvert.SerializeXmlNode(document);
object? tree = JsonConvert.DeserializeObject<JObject>(json)?.ToObject<object>();
var serialiser = new SerializerBuilder()
.WithDefaultScalarStyle(ScalarStyle.SingleQuoted) // quote everything
.Build();
Console.Write(serialiser.Serialize(tree));
// WithDefaultScalarStyle is blunt: every scalar comes out quoted, including
// the ones that did not need it. That is the right trade for generated data.
// Drop it only if you are hand-checking the output.# yq v4 (Mike Farah) converts directly and quotes ambiguous scalars.
yq -p=xml -o=yaml '.' document.xml
# Match the key convention used on this page:
yq -p=xml -o=yaml \
--xml-attribute-prefix='attr_' \
--xml-content-name='text' \
'.' document.xml > out.yaml
# Then load it back with the parser that will actually consume it. This is
# the only check that proves nothing changed type on the way through:
python -c "import yaml; print(yaml.safe_load(open('out.yaml'))['order']['country'])"
# expect: NO not: FalseEl fallo del que habla esta página es silencioso. Un archivo YAML con un NO sin comillas se analiza limpiamente, valida limpiamente y despliega limpiamente; el país simplemente es false desde entonces. La comprobación que lo pilla es cargar el archivo generado de vuelta con la misma biblioteca que usa el consumidor y comparar un valor conocido por incómodo, no leer el diff.
Preguntas frecuentes
¿Se sube mi XML cuando lo convierto a YAML?
No. El escáner de XML, el mapeador de árbol y el emisor de YAML son todos JavaScript en esta pestaña, y no hay endpoint al que puedan publicar. Abre la pestaña Red de tus herramientas de desarrollo, pega un documento y mira cómo no pasa nada.
Vale confirmarlo en vez de suponerlo, porque el XML convertido a YAML es muy a menudo configuración. Cadenas de conexión, cuentas de servicio, claves de API y nombres de host internos acaban todos en esa clase de documento que la gente trae a un conversor.
¿Qué es el problema de Noruega?
YAML 1.1 define su tipo booleano como una lista fija de escrituras, y esa lista incluye n, N, no, No y NO. Así que un campo que lleva el código ISO de Noruega, escrito sin comillas, se carga como false. La misma lista se traga y e Y, on y off, y cualquier columna Sí/No exportada de una hoja de cálculo.
YAML 1.2 estrechó el esquema central a solo true y false, lo cual no ha arreglado el ecosistema: PyYAML, Psych, Ansible y buena parte de las herramientas de Kubernetes siguen resolviendo el conjunto de 1.1, y rara vez controlas qué analizador lee tu archivo. El emisor entrecomilla todas las escrituras de esa lista, así que NO se queda como la cadena NO.
¿Por qué algunos valores van entre comillas y otros no?
Porque las comillas son estructurales. Un escalar YAML plano recibe su tipo según cómo esté escrito, así que 01730 es un número, 1.10 es un flotante, NO es un booleano y un guion inicial empieza un elemento de lista. Entrecomillar es la forma de decir que es texto.
El emisor entrecomilla exactamente los valores que de otro modo cambiarían de tipo o de significado y deja el resto llano, porque entrecomillar todos los escalares hace el archivo más difícil de leer y de comparar sin ningún beneficio. Para un entrecomillado uniforme, la mayoría de las bibliotecas de YAML tienen una opción de forzar comillas; los ejemplos de arriba la muestran para js-yaml y YamlDotNet.
¿Qué pasa con el contenido de texto de varias líneas?
Se convierte en un escalar de bloque literal, introducido por una barra vertical con indicador de recorte, con las líneas indentadas debajo. Se eligió literal sobre plegado a propósito: un bloque plegado refluye los saltos de línea sencillos convirtiéndolos en espacios, destruyendo calladamente código y direcciones incrustados.
Un caso que vigilar. Si la primera línea del texto está indentada más que las de después, lo que pasa cuando una sección CDATA conserva los espacios iniciales, el bloque es ambiguo y un analizador lo rechazará.
¿Los elementos repetidos se convierten en listas de YAML?
Sí. Un elemento que aparece más de una vez bajo el mismo padre se convierte en una secuencia escrita como lista de guiones; uno que aparece una vez se convierte en un mapeo anidado llano o en un escalar. Esa es la misma ambigüedad del singleton que describe la página de XML a JSON, y aquí es más peligrosa porque YAML la esconde: la diferencia entre un elemento y dos es un guion y dos espacios de indentación.
Usa el campo «siempre un array» encima del editor. Nombra los elementos que conceptualmente son listas y se emiten como secuencias tanto si el documento lleva uno como si lleva cuarenta.
¿Se conservan los comentarios y los espacios de nombres de XML?
Los comentarios no. Se descartan al mapear el documento a un árbol, antes de que el emisor vea nada. Los comentarios de YAML no forman parte del modelo de datos, así que uno escrito en la salida desaparecería la primera vez que alguien cargara y volviera a guardar el archivo.
Los prefijos de espacio de nombres se mantienen literales, así que soap:Body se convierte en una clave escrita soap:Body, entrecomillada automáticamente porque los dos puntos no son legales en una clave YAML desnuda. Marcar «quitar prefijos de espacio de nombres» da un Body llano, a riesgo de fusionar dos espacios de nombres en una sola clave.