Conversor de XML a JSON

Convierte XML a JSON o JSON a XML, con atributos @, #text, arrays decididos por todo el documento y la línea y columna donde el XML no está bien formado.

Sangría
Entrada
Salida

La salida aparecerá aquí

Qué hace esta herramienta

XML suele llegar de sistemas que nadie eligió: una respuesta SOAP, un feed RSS, un archivo de configuración, una exportación de algo más antiguo que JSON. El código que tiene que usarlo quiere JSON. Lo contrario es igual de habitual: un payload JSON destinado a un sistema que solo acepta XML. Esta página convierte en las dos direcciones, en tu navegador, y lee ambos con un solo conjunto fijo de reglas, así que las dos direcciones están de acuerdo en qué significa cada clave.

Lo que la distingue es que la forma del JSON se decide a partir de todo el documento y no del elemento que se esté leyendo, y la página lo dice. Dondequiera que esa decisión da a un elemento una forma distinta de la que habría tenido por sí solo, un aviso debajo de la salida nombra la ruta y la línea y la columna en que se aplicó por primera vez. Lo que no cabe en JSON, como los comentarios o el orden del contenido mixto, se avisa de la misma manera en lugar de desaparecer, y en la otra dirección cada cambio que XML impone a tu JSON se nombra en el lugar donde ocurrió.

Las reglas: @ para un atributo, #text para el texto

Hay un solo conjunto de reglas, y ningún ajuste lo cambia. Un atributo se convierte en una clave escrita como @ más el nombre del atributo, y el texto que tiene que compartir su elemento con cualquier otra cosa va bajo la clave #text. Ni @ ni # pueden empezar un nombre XML, así que ninguna clave escrita de este modo puede confundirse con un elemento hijo, y eso es lo que permite volver a leer el mismo JSON como XML sin adivinar. Aplicado a un pequeño catálogo:

  • El elemento raíz es la única clave del nivel superior: un documento cuyo elemento raíz es catalog se convierte en un objeto con la única clave "catalog".
  • Un elemento que no contiene más que texto es ese texto: <title>Midnight Rain</title> se convierte en "title": "Midnight Rain".
  • Un atributo es una clave junto a los elementos hijo: <book id="bk101"> da al libro "@id": "bk101", y cada elemento hijo sigue como clave, en el orden en que aparece por primera vez.
  • Un elemento con atributos y texto guarda el texto bajo #text: <price currency="USD">44.95</price> se convierte en "price": {"@currency": "USD", "#text": 44.95}.
  • Un elemento que se repite bajo un mismo padre se convierte en un array: un libro cuyo elemento author aparece dos veces tiene "author": ["Gambardella, Matthew", "Knorr, Stefan"].
  • Los nombres se conservan exactamente como están escritos, prefijo incluido: <dc:title> se convierte en la clave "dc:title", una declaración de espacio de nombres se convierte en una clave de atributo como "@xmlns:dc", y nada se resuelve a un URI de espacio de nombres.

Las entidades y las referencias de carácter se decodifican, así que &amp; llega como & y &#233; como é, y una sección CDATA es simplemente texto. El espacio en blanco aislado entre elementos es formato y se descarta; cualquier otro tramo de texto se conserva entero y nunca se recorta. Las dos normalizaciones que aplica todo analizador de XML conforme siguen yendo primero: un retorno de carro escrito en el texto llega como salto de línea, y un tabulador o un salto de línea escritos en un valor de atributo llegan como espacio. Una referencia de carácter es la forma en que un documento conserva cualquiera de los dos.

Por qué todo el documento decide la forma

Un conversor que decide elemento por elemento escribe los autores del primer libro como un array, porque son dos, y el autor del segundo como una cadena, porque es uno. Un elemento que lleva un atributo es un objeto en un registro y texto simple en el siguiente. El código escrito para el primer registro falla con el segundo, y nada por el camino dijo que la forma podía cambiar.

Por eso la forma se decide por ruta: los nombres de los elementos desde la raíz hasta un elemento, escritos /catalog/book/author, sin posiciones, de modo que cada autor de cada libro está en una misma ruta. Todos los elementos de una ruta se sopesan a la vez:

  • Arrays. Si el elemento se repite bajo cualquier padre, cada elemento de la ruta se escribe como array, incluido uno solitario.
  • Objetos. Si algún elemento de la ruta lleva un atributo o un elemento hijo, cada elemento de ella se escribe como objeto, incluido uno que solo tiene texto, y guarda su texto bajo #text.
  • Valores. Una ruta se lee como números solo donde todos sus valores se convierten, y como booleanos solo donde todos los valores son true o false, según la regla que se describe más abajo, en números y booleanos.
  • Elementos vacíos. Un elemento vacío no participa en estas decisiones, y toma la clase por la que se decide su ruta.

Dondequiera que el resultado difiere de lo que un elemento habría sido por sí solo, la página lo dice una vez por ruta, con la línea y la columna del primer elemento al que cambió. En un catálogo donde un libro tiene varios autores y otro solo uno, ese único autor sigue siendo un array; donde un precio lleva un atributo currency y otro no, el precio simple sigue siendo un objeto, con su texto bajo #text. Ambos se avisan, así que ninguno sorprende al código que los lee.

Un ejemplo con un solo registro y el campo «Siempre arrays»

La forma solo puede ser tan buena como el documento a partir del cual se decide. Pega un catálogo con un solo libro y ese libro es un objeto, su único autor una cadena y su precio un número, mientras que el mismo catálogo con un segundo libro al lado convierte los libros en un array. Una ruta que en todo lo que pegaste aparece una vez en cada sitio sigue siendo un solo valor, y una ruta cuyos valores da la casualidad de que se convierten todos sigue siendo de números hasta que llega un documento con un código postal que empieza por cero. El JSON es exactamente tan fiable como representativo es el ejemplo, así que pega más de un registro siempre que puedas.

Para los arrays hay un remedio que no depende del ejemplo. Escribe en «Siempre arrays» los nombres de elemento o las rutas que deben ser arrays muestre lo que muestre el documento, separados por comas o espacios, y cada ruta con la que coincida un término se escribe como array en todas partes, incluso donde aparece una sola vez:

  • Un nombre a secas como item coincide con toda ruta que termina en un elemento llamado item, esté donde esté.
  • Una ruta como /rss/channel/item coincide con esa única ruta, escrita como se escriben las rutas debajo de la salida, así que se puede copiar una de ahí.
  • La coincidencia es exacta: cuentan las mayúsculas y minúsculas, y un prefijo forma parte del nombre, así que Item no coincide con ningún item y link no coincide con ningún atom:link.
  • Cada ruta que el campo convirtió en array se avisa como cualquier otra decisión. Un término que no es ni un nombre ni una ruta, y uno que no coincidió con ningún elemento del documento, se enumeran debajo del campo, y los términos que sí se aplican siguen aplicándose.

El campo pertenece a la dirección «XML a JSON». Conserva lo que escribiste cuando cambias de dirección y vuelves, y, como todos los ajustes de la página, no se guarda entre visitas. Los valores no tienen un campo así: una ruta que debe seguir siendo texto contenga lo que contenga el ejemplo es para lo que sirve desactivar «Convertir números y booleanos», al precio de que todos los demás valores pasen también a ser texto.

Números, booleanos y por qué 02134 sigue siendo texto

En XML todo es texto, y convertir en números JSON el texto que parece numérico suele ser lo que quieres. También es la forma en que un código postal 02134 se convierte en 2134, y en que un número de pedido largo pierde sus últimos dígitos. La regla aquí es la misma que usa el conversor de CSV de este sitio, y no necesita ninguna lista de casos especiales: el texto se convierte en número solo donde ese número, escrito de nuevo, reproduce exactamente el texto. 42 lo hace, así que se convierte. 02134 no, ni tampoco 1.50, +5, 1e5 o un entero demasiado largo para que un número de doble precisión conserve todos sus dígitos. Los booleanos son exactamente true y false, en minúsculas.

Lo que añade esta página es que la regla se aplica a toda una ruta a la vez. Una ruta se lee como números solo si todos sus valores se convierten, y como booleanos solo si todos los valores lo son; un solo valor que perdería un dígito, o números y booleanos que se encuentran en una misma ruta, dejan como texto todos los valores de ahí. Cada nombre de atributo de una ruta se decide por separado. Una ruta que se deja como texto aunque algunos de sus valores se habrían convertido se avisa, nombrando la ruta, y para un atributo la ruta termina en el atributo, como en /catalog/book/@id.

«Convertir números y booleanos» está activado cuando se abre la página, igual que lo está el interruptor equivalente en la página de CSV, y al desactivarlo todos los valores se escriben como texto. El valor de una declaración de espacio de nombres nunca se lee como número, esté como esté el interruptor, porque el estándar Namespaces in XML define ese valor como un nombre de espacio de nombres, que es texto.

Elementos vacíos y el null que indica el propio documento

  • Un elemento vacío toma la clase por la que se decide su ruta. En una ruta de texto es la cadena vacía. En una ruta de números o booleanos es null, y la página lo dice, ya que por sí solo el elemento habría sido texto vacío.
  • Vacío significa nada en absoluto: <zip> </zip> contiene un espacio, y un espacio es texto.
  • Una ruta en la que todos los elementos están vacíos es de texto, así que cada uno de ellos es la cadena vacía. Nada ahí se lee como número, y un null sería información que el documento nunca dio.
  • En una ruta escrita como objetos, un elemento vacío también lleva #text, vacío o null según la misma regla, dondequiera que otro elemento de la ruta tenga texto: junto a <price currency="USD">5</price>, un <price/> se convierte en {"#text": null}. Donde ningún elemento de la ruta tiene texto, como con elementos de imagen que solo llevan un atributo src, ninguno de ellos lleva #text.

Un documento también puede indicar null por sí mismo. Un elemento marcado con xsi:nil="true", con ese prefijo vinculado al espacio de nombres XML Schema instance, es null en cualquier ruta, esté como esté «Convertir números y booleanos». La marca es la forma en que el documento dice null y no datos, así que en el JSON no aparecen ni el atributo ni una declaración de espacio de nombres que no vincule nada más. Sirve cualquier prefijo vinculado a ese espacio de nombres, e i:nil="1" dice lo mismo; xsi:nil="false" son datos corrientes, y un elemento marcado como nil que aún tiene contenido se deja tal cual, ya que leerlo como null descartaría ese contenido sin decir nada.

Lo que no cabe en JSON, avisado en lugar de perdido

Parte de lo que contiene un documento XML no tiene sitio en JSON. Lo que tiene significado se avisa debajo de la salida en lugar de descartarse en silencio:

  • El orden del contenido mixto. En <p>Hello <b>world</b>, again</p> el texto se reúne bajo #text como "Hello , again" y el elemento b bajo "b": sobreviven todos los caracteres y todos los elementos, pero no el orden en que estaban. Los elementos hijo que alternan entre nombres pierden su orden del mismo modo. Se avisa una vez para la ruta del elemento cuyo contenido es, y solo donde el orden se perdió de verdad, así que <p><b>Note:</b> the rest</p> no provoca nada, porque el orden de sus claves lo conserva.
  • Los comentarios y las instrucciones de procesamiento. Se omiten, con un aviso para cada clase que dice cuántas apariciones hubo y dónde está la primera.
  • El DOCTYPE. También se omite, y su aviso dice que la DTD no se aplicó: un valor por defecto que la DTD declara para un atributo falta en el JSON, porque esta herramienta no lee la DTD.
  • La declaración XML del principio. Se omite sin aviso, porque describe cómo está codificado el texto y no qué son los datos.

JSON de vuelta a XML: el XML más cercano, y cada cambio nombrado

Cambia de dirección y las mismas reglas funcionan al revés: una clave @ se convierte en un atributo, #text en el texto de su elemento, un array en un elemento por cada miembro, y cualquier otra clave en un elemento hijo, con los elementos hijo en el orden en que están sus claves. Un null se escribe como un elemento vacío marcado con xsi:nil="true", con el espacio de nombres XML Schema instance declarado una vez en el elemento raíz y solo cuando se escribe un null, así que al convertir de vuelta se obtiene otra vez null y no una cadena vacía. Salvo el JSON que no se puede analizar, o que anida a demasiada profundidad para convertirlo, no se rechaza nada. Donde tu JSON tiene una forma que XML no puede llevar tal cual, se escribe el XML bien formado más cercano y el cambio se nombra debajo de la salida, en un JSONPath que puedes pegar en el Probador de JSONPath de este sitio:

  • Cualquier cosa que no sea un único elemento raíz, ya sean varias claves en el nivel superior, ninguna, un array de nivel superior o un valor suelto, se escribe dentro de <root>.
  • Una clave que no es un nombre XML se escribe con el nombre más cercano con el que no se escribe ninguna otra clave a su lado: first name como first_name, y 1st como _1st.
  • Un miembro de un array que ninguna clave nombra, ya esté dentro de otro array o en el nivel superior, se escribe como un elemento <item>.
  • Una clave @ o un #text que contiene un objeto o un array se escribe como elemento hijo, ya que ni un atributo ni un texto pueden contenerlo.
  • Una clave @ que contiene null se omite, igual que un #text que contiene null junto a elementos hijo, ya que XML solo dice null de un elemento entero.
  • Un carácter que XML 1.0 no puede contener en absoluto, ni siquiera como referencia, se reemplaza, y la página dice dónde.

El escapado forma parte de escribir XML bien formado, no es una opción. Un < y un & se escapan siempre. En un valor de atributo también se escapa una comilla doble, porque el valor se escribe entre dos de ellas; un tabulador, un salto de línea o un retorno de carro ahí se escribe como referencia de carácter, porque de lo contrario un analizador lo convertiría en un espacio. En el texto, un retorno de carro se escribe como referencia, porque de lo contrario un analizador lo convertiría en un salto de línea, y ]]> nunca se escribe tal cual. Eso es lo que permite que el valor se lea de vuelta como el valor que escribiste.

Lo que da la conversión de vuelta, dicho antes de que conviertas

Hay XML que está bien formado y aun así se lee de vuelta como algo distinto del JSON a partir del cual se escribió, así que la página lee el XML que acaba de escribir, con las mismas reglas que usa la otra dirección, y dice dónde difiere la respuesta. Un array de un solo miembro se lee de vuelta como su único miembro, a menos que los mismos elementos se repitan bajo un mismo padre en otro lugar del documento. Un array vacío no escribe ningún elemento y un #text vacío no escribe ningún texto, así que cada uno se lee de vuelta como nada. Un objeto que no contiene nada que se escriba como atributo o elemento hijo se lee de vuelta como un valor simple, y donde los mismos elementos en otro lugar hacen que el documento decida un array o un objeto que tu JSON no tenía, eso también se dice. Cada uno de estos casos se escribe como lo tiene tu JSON, y se avisa.

«Usar como entrada» mueve la salida al campo de entrada y cambia la dirección, así que lo que vuelve, y lo que avisa, está a un clic. Donde no se avisa nada, lo que vuelve es tu JSON, salvo lo que XML no guarda. El texto XML no deja constancia de si un valor era un número, un booleano o una cadena: con «Convertir números y booleanos» activado, una cadena que reproduce exactamente un número puede volver como ese número, y con él desactivado todos los valores vuelven como texto. Y un atributo, o un #text vacío o null, puede volver en otro lugar entre las claves que tiene al lado, porque XML no guarda esa posición.

La ida y vuelta que parte del XML promete menos, a propósito. El texto que pegaste no vuelve: la declaración, el formato, los comentarios y todo lo demás que se avisó a la ida desaparecen. Lo que se mantiene estable es el XML que escribe la página: conviértelo a JSON y de vuelta, con los mismos ajustes, y vuelve el mismo XML.

Cuando el XML o el JSON no se puede leer

Un documento que el analizador de XML no puede leer se rechaza con una frase que dice qué está mal, y una línea y una columna dondequiera que haya un lugar que dar: un elemento que nunca se cierra, un valor de atributo sin comillas y un & que no inicia ninguna referencia tienen cada uno su propia frase. Una etiqueta de cierre que no coincide con el elemento aún abierto lo dice, y luego muestra la etiqueta de cierre que ese elemento necesita, que nombra el elemento que quedó abierto. El JSON que no se puede analizar se rechaza de la misma manera, con la línea y la columna donde se detuvo su analizador dondequiera que eso se pueda medir.

Debajo de una frase que da un lugar, la página muestra tu propia línea con el carácter que falla marcado, lo que importa sobre todo en XML o JSON minificado, donde todo el documento es una sola línea. Una línea larga se recorta al tramo que rodea la marca, con puntos suspensivos donde se cortó. Un carácter que falla y no dibuja nada, como el final del texto, un tabulador o una marca de orden de bytes, se marca sobre un sustituto visible. La columna cuenta como JavaScript cuenta una cadena, en unidades de código UTF-16, así que un emoji o un ideograma poco común antes de la marca la desplaza en dos en lugar de en uno.

Un documento anidado a más profundidad de la que permite el sitio también se rechaza, en cualquier dirección, y su frase indica el límite.

Preguntas frecuentes

¿Por qué un solo elemento se escribe como array?
Porque un elemento de la misma ruta se repite bajo un mismo padre en otro lugar del documento, y la ruta se decide una vez para todo él. El código que lee el valor como array funciona entonces para cada registro, incluidos los que tienen un solo elemento. La página nombra la ruta, con la línea y la columna del primer elemento al que esto cambió. Para obtener un array donde tu ejemplo solo tiene uno, escribe el elemento en «Siempre arrays».
¿Por qué un número está entre comillas en mi JSON?
Porque algún valor de la misma ruta no sobreviviría como número, ya sea por un cero inicial, un cero final tras el punto decimal, un signo más o un exponente, o porque en esa ruta se encuentran números y booleanos. Una ruta así es texto en todas partes, y la página la nombra. Sus valores se convierten en cuanto todos ellos están escritos como se escribe el número de nuevo: 2134 en lugar de 02134.
¿Qué pasa con los comentarios, las instrucciones de procesamiento y el DOCTYPE?
Se omiten del JSON, y cada clase se avisa con cuántas apariciones hubo y dónde está la primera. El aviso del DOCTYPE dice además que la DTD no se aplicó, así que falta cualquier valor por defecto de atributo que declare.
¿Comprueba mi XML contra una DTD o un XSD?
No. Lee XML bien formado y dice dónde no pudo leer un documento, pero no lo valida contra una DTD ni un XSD, y no aplica nada de lo que declara una DTD. Por eso un DOCTYPE se avisa como no aplicado en lugar de ignorarse en silencio.
¿Cómo se escribe null en XML, y vuelve?
Como un elemento vacío marcado con xsi:nil="true", con el espacio de nombres XML Schema instance declarado una vez en el elemento raíz. Al convertir ese XML de vuelta se obtiene otra vez null, mientras que un elemento vacío simple habría vuelto como cadena vacía en cualquier ruta leída como texto. Si tu JSON ya usa el prefijo xsi para un nombre suyo, la página declara en su lugar un prefijo libre.
¿Qué pasa con una clave que tiene un espacio?
Se escribe con el nombre más cercano que XML admite, así que first name se convierte en first_name, y la página nombra la clave por su JSONPath junto con el nombre en que se convirtió. Pega ese JSONPath en el Probador de JSONPath de este sitio para seleccionar todos los valores a los que se aplicó el cambio.
¿Por qué al convertir de vuelta no obtengo mi XML original?
Porque JSON no tiene sitio para parte de lo que contiene un archivo XML: la declaración, el formato entre elementos, los comentarios, las instrucciones de procesamiento, el DOCTYPE y el orden del contenido mixto. Lo que tenía significado se avisó a la ida. Lo que se mantiene estable es el XML que escribe la página: llévalo a JSON y de vuelta, con los mismos ajustes, y vuelve el mismo XML.
¿Se envía a un servidor algo de lo que pego?
No. Las dos direcciones se ejecutan por completo en tu navegador, así que el documento que pegas, ya sea una respuesta de una API, un feed o un archivo de configuración con credenciales dentro, nunca sale de tu dispositivo.

Herramientas relacionadas