Convertisseur XML vers JSON

Convertissez XML en JSON ou JSON en XML : attributs en @, #text, tableaux décidés par tout le document, et ligne et colonne où le XML est mal formé.

Indentation
Entrée
Sortie

La sortie apparaîtra ici

Ce que fait cet outil

Le XML a tendance à venir de systèmes que personne n’a choisis : une réponse SOAP, un flux RSS, un fichier de configuration, un export de quelque chose de plus ancien que JSON. Le code qui doit s’en servir veut du JSON. L’inverse est tout aussi courant : une charge utile JSON destinée à un système qui n’accepte que du XML. Cette page convertit dans les deux sens, dans votre navigateur, et lit les deux formats selon un seul ensemble fixe de règles, si bien que les deux sens s’accordent sur ce que signifie chaque clé.

Ce qui la distingue, c’est que la forme du JSON est décidée à partir du document entier plutôt qu’à partir de l’élément en cours de lecture, et que la page le dit. Partout où cette décision donne à un élément une autre forme que celle qu’il aurait eue seul, un avis sous la sortie nomme le chemin, ainsi que la ligne et la colonne où elle s’est appliquée pour la première fois. Ce pour quoi JSON n’a pas de place, comme les commentaires ou l’ordre d’un contenu mixte, est signalé de la même façon au lieu de disparaître, et dans l’autre sens chaque réécriture que XML impose à votre JSON est nommée à l’endroit où elle s’est produite.

Les règles : @ pour un attribut, #text pour le texte

Il y a un seul ensemble de règles, et aucun réglage ne le modifie. Un attribut devient une clé qui s’écrit @ suivi du nom de l’attribut, et le texte qui doit partager son élément avec quoi que ce soit d’autre va sous la clé #text. Ni @ ni # ne peuvent commencer un nom XML, donc aucune clé écrite ainsi ne peut être prise pour un élément enfant, et c’est ce qui permet de relire le même JSON en XML sans rien deviner. Appliquées à un petit catalogue :

  • L’élément racine est l’unique clé du premier niveau : un document dont l’élément racine est catalog devient un objet dont la seule clé est "catalog".
  • Un élément qui ne contient que du texte est ce texte : <title>Midnight Rain</title> devient "title": "Midnight Rain".
  • Un attribut est une clé à côté des éléments enfants : <book id="bk101"> donne au livre "@id": "bk101", et chaque élément enfant suit sous forme de clé, dans l’ordre de sa première apparition.
  • Un élément qui a des attributs et du texte garde le texte sous #text : <price currency="USD">44.95</price> devient "price": {"@currency": "USD", "#text": 44.95}.
  • Un élément qui se répète sous un même parent devient un tableau : un livre dont l’élément author apparaît deux fois a "author": ["Gambardella, Matthew", "Knorr, Stefan"].
  • Les noms sont gardés exactement tels qu’ils sont écrits, préfixe compris : <dc:title> devient la clé "dc:title", une déclaration d’espace de noms devient une clé d’attribut comme "@xmlns:dc", et rien n’est résolu en URI d’espace de noms.

Les entités et les références de caractère sont décodées, si bien que &amp; arrive sous la forme & et &#233; sous la forme é, et une section CDATA est simplement du texte. Les espaces vides isolés entre des éléments sont de la mise en forme et sont supprimés ; toute autre suite de texte est gardée en entier, sans jamais être rognée. Les deux normalisations qu’applique tout analyseur XML conforme passent malgré tout en premier : un retour chariot tapé dans du texte arrive comme un saut de ligne, et une tabulation ou une fin de ligne tapées dans une valeur d’attribut arrivent comme une espace. C’est par une référence de caractère qu’un document garde l’un ou l’autre.

Pourquoi le document entier décide de la forme

Un convertisseur qui décide élément par élément écrit les auteurs du premier livre comme un tableau, parce qu’ils sont deux, et l’auteur du second comme une chaîne, parce qu’il n’y en a qu’un. Un élément qui porte un attribut est un objet dans un enregistrement et du texte simple dans le suivant. Le code écrit pour le premier enregistrement casse sur le second, et rien, en cours de route, n’a dit que la forme pouvait changer.

La forme est donc décidée par chemin : les noms d’éléments depuis la racine jusqu’à un élément, écrits /catalog/book/author, sans aucune position, de sorte que chaque auteur de chaque livre se trouve sur un seul chemin. Tous les éléments d’un chemin sont pesés à la fois :

  • Tableaux. Si l’élément se répète sous un même parent, quel qu’il soit, chaque élément du chemin est écrit comme un tableau, y compris un élément seul.
  • Objets. Si l’un des éléments du chemin porte un attribut ou un élément enfant, chaque élément de ce chemin est écrit comme un objet, y compris celui qui ne contient que du texte, et garde son texte sous #text.
  • Valeurs. Un chemin n’est lu comme des nombres que là où chacune de ses valeurs se convertit, et comme des booléens que là où chaque valeur est true ou false, selon la règle décrite plus bas pour les nombres et les booléens.
  • Éléments vides. Un élément vide ne prend aucune part à ces décisions, et prend la nature que retient son chemin.

Partout où le résultat diffère de ce qu’un élément aurait été seul, la page le dit une fois pour le chemin, avec la ligne et la colonne du premier élément que cela a changé. Dans un catalogue où un livre a plusieurs auteurs et un autre un seul, cet auteur unique reste un tableau ; là où un prix porte un attribut currency et un autre non, le prix simple reste un objet, son texte sous #text. Les deux sont signalés, si bien qu’aucun ne surprend le code qui les lit.

Un exemple à un seul enregistrement, et le champ « Toujours des tableaux »

La forme ne peut valoir que ce que vaut le document à partir duquel elle est décidée. Collez un catalogue qui contient un seul livre et ce livre est un objet, son unique auteur une chaîne et son prix un nombre, alors que le même catalogue avec un second livre à côté fait des livres un tableau. Un chemin qui n’apparaît qu’une fois partout dans ce que vous avez collé reste une valeur unique, et un chemin dont toutes les valeurs se trouvent être convertibles reste un chemin de nombres jusqu’à ce qu’arrive un document contenant un code postal qui commence par un zéro. Le JSON est exactement aussi fiable que l’exemple est représentatif, alors collez plus d’un enregistrement chaque fois que vous le pouvez.

Pour les tableaux, il existe un remède qui ne dépend pas de l’exemple. Saisissez dans « Toujours des tableaux » les noms d’éléments ou les chemins qui doivent être des tableaux quoi que montre le document, séparés par des virgules ou des espaces, et chaque chemin qu’atteint une indication est écrit comme un tableau partout, même là où il n’apparaît qu’une fois :

  • Un nom seul comme item atteint tout chemin qui se termine par un élément nommé item, où qu’il se trouve.
  • Un chemin comme /rss/channel/item atteint ce seul chemin, écrit comme s’écrivent les chemins sous la sortie, si bien qu’on peut en copier un depuis là.
  • La correspondance est exacte : la casse compte, et un préfixe fait partie du nom, donc Item n’atteint aucun item et link n’atteint aucun atom:link.
  • Chaque chemin que le champ a changé en tableau est signalé comme toute autre décision. Une indication qui n’est ni un nom ni un chemin, et une indication qui n’a atteint aucun élément du document, sont listées sous le champ, et les indications qui s’appliquent, elles, continuent de s’appliquer.

Le champ appartient au sens « XML vers JSON ». Il garde ce que vous y avez saisi quand vous changez de sens puis revenez, et comme tous les réglages de la page il n’est pas conservé d’une visite à l’autre. Les valeurs n’ont pas de tel champ : un chemin qui doit rester du texte quoi que contienne l’exemple, c’est à cela que sert la désactivation de « Convertir nombres et booléens », au prix de voir toutes les autres valeurs devenir du texte elles aussi.

Nombres, booléens, et pourquoi 02134 reste du texte

En XML, tout est du texte, et convertir en nombres JSON un texte qui a l’air numérique est en général ce que vous voulez. C’est aussi ainsi qu’un code postal 02134 devient 2134, et qu’un long numéro de commande perd ses derniers chiffres. La règle ici est celle qu’utilise le convertisseur CSV de ce site, et elle n’a besoin d’aucune liste de cas particuliers : un texte ne devient un nombre que là où ce nombre, réécrit, reproduit exactement le texte. 42 le fait, donc il est converti. 02134 non, pas plus que 1.50, +5, 1e5 ou un entier trop long pour qu’un flottant double précision en garde tous les chiffres. Les booléens sont exactement true et false, en minuscules.

Ce que cette page ajoute, c’est que la règle est appliquée à tout un chemin à la fois. Un chemin n’est lu comme des nombres que si chacune de ses valeurs se convertit, et comme des booléens que si chaque valeur en est un ; une seule valeur qui perdrait un chiffre, ou des nombres et des booléens qui se rencontrent sur un même chemin, y gardent toutes les valeurs en texte. Chaque nom d’attribut d’un chemin est décidé à part. Un chemin gardé en texte alors que certaines de ses valeurs se seraient converties est signalé, en nommant le chemin, et pour un attribut le chemin se termine sur l’attribut, comme dans /catalog/book/@id.

« Convertir nombres et booléens » est activé à l’ouverture de la page, comme l’est l’interrupteur équivalent sur la page CSV, et le désactiver écrit toutes les valeurs en texte. La valeur d’une déclaration d’espace de noms n’est jamais lue comme un nombre, dans un cas comme dans l’autre, car la norme Namespaces in XML définit cette valeur comme un nom d’espace de noms, qui est du texte.

Éléments vides, et le null qu’un document indique lui-même

  • Un élément vide prend la nature que retient son chemin. Sur un chemin de texte, c’est la chaîne vide. Sur un chemin de nombres ou de booléens, c’est null, et la page le dit, puisque, pris isolément, l’élément aurait été du texte vide.
  • Vide veut dire rien du tout : <zip> </zip> contient une espace, et une espace est du texte.
  • Un chemin dont tous les éléments sont vides est du texte, donc chacun d’eux est la chaîne vide. Rien ne s’y lit comme un nombre, et un null serait une information que le document n’a jamais donnée.
  • Sur un chemin écrit en objets, un élément vide porte lui aussi #text, vide ou null selon la même règle, partout où un autre élément du chemin contient du texte : à côté de <price currency="USD">5</price>, un <price/> devient {"#text": null}. Là où aucun élément du chemin ne contient de texte, comme pour des éléments d’image qui ne portent qu’un attribut src, aucun d’eux ne porte #text.

Un document peut aussi indiquer null lui-même. Un élément marqué xsi:nil="true", avec ce préfixe lié à l’espace de noms XML Schema instance, est null sur n’importe quel chemin, quel que soit le réglage de « Convertir nombres et booléens ». Ce marquage est la façon dont le document dit null plutôt que des données, donc ni l’attribut ni une déclaration d’espace de noms qui ne lie rien d’autre n’apparaissent dans le JSON. Tout préfixe lié à cet espace de noms fonctionne, et i:nil="1" dit la même chose ; xsi:nil="false" est une donnée ordinaire, et un élément marqué nil qui a pourtant un contenu est laissé tel quel, puisque le lire comme null supprimerait ce contenu sans un mot.

Ce pour quoi JSON n’a pas de place, signalé au lieu d’être perdu

Une partie de ce que contient un document XML n’a pas de place en JSON. Ce qui porte du sens est signalé sous la sortie plutôt que supprimé en silence :

  • L’ordre d’un contenu mixte. Dans <p>Hello <b>world</b>, again</p> le texte est regroupé sous #text en "Hello , again" et l’élément b sous "b" : chaque caractère et chaque élément survivent, mais pas l’ordre dans lequel ils se trouvaient. Des éléments enfants dont les noms alternent perdent leur ordre de la même façon. C’est signalé une fois pour le chemin de l’élément dont c’est le contenu, et seulement là où l’ordre a vraiment été perdu, si bien que <p><b>Note:</b> the rest</p> ne déclenche rien, puisque l’ordre de ses clés le conserve.
  • Les commentaires et les instructions de traitement. Ils sont omis, avec un avis pour chaque sorte qui dit combien il y en avait et où se trouve la première occurrence.
  • Le DOCTYPE. Il est omis lui aussi, et son avis dit que la DTD n’a pas été appliquée : une valeur par défaut que la DTD déclare pour un attribut manque dans le JSON, car cet outil ne lit pas la DTD.
  • La déclaration XML en tête. Elle est omise sans avis, car elle décrit comment le texte est encodé plutôt que ce que sont les données.

Le JSON reconverti en XML : le XML le plus proche, et chaque réécriture nommée

Changez de sens, et les mêmes règles s’appliquent à l’envers : une clé @ devient un attribut, #text devient le texte de son élément, un tableau devient un élément pour chaque membre, et toute autre clé devient un élément enfant, les éléments enfants dans l’ordre où se trouvent leurs clés. Un null est écrit comme un élément vide marqué xsi:nil="true", avec l’espace de noms XML Schema instance déclaré une seule fois sur l’élément racine, et seulement quand un null est écrit, si bien que reconvertir redonne null plutôt qu’une chaîne vide. À part un JSON qui ne s’analyse pas, ou qui est imbriqué trop profondément pour être converti, rien n’est refusé. Là où votre JSON a une forme que XML ne peut pas porter telle quelle, le XML bien formé le plus proche est écrit et la réécriture est nommée sous la sortie, à un JSONPath que vous pouvez coller dans le Testeur JSONPath de ce site :

  • Tout ce qui n’est pas un seul élément racine, qu’il s’agisse de plusieurs clés au premier niveau, d’aucune, d’un tableau au premier niveau ou d’une valeur seule, est écrit à l’intérieur de <root>.
  • Une clé qui n’est pas un nom XML est écrite sous le nom le plus proche sous lequel aucune autre clé à côté n’est écrite : first name en first_name, et 1st en _1st.
  • Un membre d’un tableau qu’aucune clé ne nomme, qu’il se trouve dans un autre tableau ou au premier niveau, est écrit comme un élément <item>.
  • Une clé @ ou un #text qui porte un objet ou un tableau est écrit comme un élément enfant, puisque ni un attribut ni un texte ne peuvent en porter un.
  • Une clé @ qui porte null est omise, tout comme un #text qui porte null à côté d’éléments enfants, puisque XML ne dit null que d’un élément entier.
  • Un caractère que XML 1.0 ne peut pas contenir du tout, pas même sous forme de référence, est remplacé, et la page dit où.

L’échappement fait partie de l’écriture d’un XML bien formé : ce n’est pas une option. Un < et un & sont toujours échappés. Dans une valeur d’attribut, un guillemet double l’est aussi, parce que la valeur est écrite entre deux d’entre eux ; une tabulation, un saut de ligne ou un retour chariot y est écrit comme une référence de caractère, parce qu’un analyseur le transformerait sinon en espace. Dans du texte, un retour chariot est écrit comme une référence, parce qu’un analyseur le transformerait sinon en saut de ligne, et ]]> n’est jamais écrit tel quel. C’est ce qui permet de relire la valeur comme la valeur que vous avez écrite.

Ce que donne la reconversion, dit avant que vous convertissiez

Certains XML sont bien formés et se relisent pourtant comme autre chose que le JSON à partir duquel ils ont été écrits ; la page lit donc le XML qu’elle vient d’écrire, selon les mêmes règles que celles de l’autre sens, et dit où la réponse diffère. Un tableau d’un seul membre se relit comme son unique membre, sauf si les mêmes éléments se répètent sous un même parent ailleurs dans le document. Un tableau vide n’écrit aucun élément et un #text vide n’écrit aucun texte, donc chacun se relit comme rien. Un objet qui ne contient rien qui s’écrive comme attribut ou élément enfant se relit comme une valeur simple, et là où les mêmes éléments, ailleurs, font décider au document un tableau ou un objet que votre JSON n’avait pas, c’est dit aussi. Chacun de ces cas est écrit tel que votre JSON l’a, et signalé.

« Utiliser comme entrée » déplace la sortie dans le champ de saisie et change de sens, si bien que ce qui revient, et ce qui est alors signalé, est à un clic. Là où rien n’est signalé, ce qui revient est votre JSON, à part ce que XML ne garde pas. Le texte XML ne note pas si une valeur était un nombre, un booléen ou une chaîne : avec « Convertir nombres et booléens » activé, une chaîne qui reproduit exactement un nombre peut revenir sous la forme de ce nombre, et lorsqu’il est désactivé, toutes les valeurs reviennent en texte. Et un attribut, ou un #text vide ou null, peut revenir à une autre place parmi les clés à côté de lui, car XML ne garde pas cette position.

L’aller-retour qui part du XML promet moins, à dessein. Le texte que vous avez collé ne revient pas : la déclaration, la mise en forme, les commentaires et tout ce qui a été signalé à l’aller ont disparu. Ce qui reste stable, c’est le XML qu’écrit la page : convertissez-le en JSON puis reconvertissez-le, avec les mêmes réglages, et le même XML revient.

Quand le XML ou le JSON ne se lit pas

Un document que l’analyseur XML ne peut pas lire est refusé avec une phrase qui dit ce qui ne va pas, et une ligne et une colonne partout où il y a un endroit à donner : un élément qui n’est jamais fermé, une valeur d’attribut sans guillemets et un & qui ne commence aucune référence ont chacun leur propre phrase. Une balise fermante qui ne correspond pas à l’élément encore ouvert le dit, puis montre la balise fermante dont cet élément a besoin, qui nomme l’élément resté ouvert. Un JSON qui ne s’analyse pas est refusé de la même façon, avec la ligne et la colonne où son analyseur s’est arrêté, partout où cela peut être mesuré.

Sous une phrase qui donne un endroit, la page montre votre propre ligne avec le caractère fautif marqué, ce qui compte surtout pour du XML ou du JSON minifié, où le document entier est une seule ligne. Une longue ligne est réduite au passage qui entoure le repère, avec des points de suspension là où elle a été coupée. Un caractère fautif qui ne dessine rien, comme la fin du texte, une tabulation ou une marque d’ordre des octets, est marqué sur un symbole visible qui en tient lieu. La colonne compte comme JavaScript compte une chaîne, en unités de code UTF-16, si bien qu’un emoji ou un idéogramme rare avant le repère la décale de deux plutôt que d’un.

Un document imbriqué plus profondément que le site ne le permet est refusé lui aussi, dans un sens comme dans l’autre, et sa phrase indique la limite.

Questions fréquentes

Pourquoi un élément seul est-il écrit comme un tableau ?
Parce qu’un élément du même chemin se répète sous un même parent ailleurs dans le document, et que le chemin est décidé une fois pour tout le document. Le code qui lit la valeur comme un tableau fonctionne alors pour chaque enregistrement, y compris ceux qui n’ont qu’un seul élément. La page nomme le chemin, avec la ligne et la colonne du premier élément que cela a changé. Pour obtenir un tableau là où votre exemple n’en contient qu’un, nommez l’élément dans « Toujours des tableaux ».
Pourquoi un nombre est-il entre guillemets dans mon JSON ?
Parce qu’une valeur du même chemin ne survivrait pas en tant que nombre, que ce soit à cause d’un zéro initial, d’un zéro final après le point décimal, d’un signe plus ou d’un exposant, ou parce que des nombres et des booléens se rencontrent sur ce chemin. Un tel chemin est du texte partout, et la page le nomme. Ses valeurs se convertissent dès que chacune d’elles est écrite comme le nombre se réécrit, 2134 plutôt que 02134.
Qu’advient-il des commentaires, des instructions de traitement et du DOCTYPE ?
Ils sont omis du JSON, et chaque sorte est signalée avec le nombre de ses occurrences et l’endroit où se trouve la première. L’avis sur le DOCTYPE dit aussi que la DTD n’a pas été appliquée, donc toute valeur d’attribut par défaut qu’elle déclare est absente.
Vérifie-t-il mon XML par rapport à une DTD ou à un XSD ?
Non. Il lit du XML bien formé et dit où il n’a pas pu lire un document, mais il ne valide pas par rapport à une DTD ou à un XSD, et il n’applique rien de ce que déclare une DTD. C’est pourquoi un DOCTYPE est signalé comme non appliqué plutôt que discrètement ignoré.
Comment null est-il écrit en XML, et revient-il ?
Comme un élément vide marqué xsi:nil="true", avec l’espace de noms XML Schema instance déclaré une seule fois sur l’élément racine. Reconvertir ce XML redonne null, là où un simple élément vide serait revenu comme une chaîne vide sur tout chemin lu comme du texte. Si votre JSON utilise déjà le préfixe xsi pour un nom qui lui est propre, la page déclare un préfixe libre à la place.
Qu’advient-il d’une clé qui contient une espace ?
Elle est écrite sous le nom le plus proche que XML autorise, donc first name devient first_name, et la page nomme la clé par son JSONPath, avec le nom qu’elle a pris. Collez ce JSONPath dans le Testeur JSONPath de ce site pour sélectionner chaque valeur à laquelle la réécriture s’est appliquée.
Pourquoi la reconversion ne me rend-elle pas mon XML d’origine ?
Parce que JSON n’a pas de place pour une partie de ce que contient un fichier XML : la déclaration, la mise en forme entre les éléments, les commentaires, les instructions de traitement, le DOCTYPE et l’ordre d’un contenu mixte. Ce qui portait du sens a été signalé à l’aller. Ce qui reste stable, c’est le XML qu’écrit la page : passez-le en JSON puis reconvertissez-le, avec les mêmes réglages, et le même XML revient.
Ce que je colle est-il envoyé à un serveur ?
Non. Les deux sens de conversion s’exécutent entièrement dans votre navigateur : le document que vous collez, qu’il s’agisse d’une réponse d’API, d’un flux ou d’un fichier de configuration contenant des identifiants de connexion, ne quitte jamais votre appareil.

Outils connexes