What does the JSON to XML converter do?
The JSON to XML converter takes a JSON document and writes the same data as well-formed XML. Objects become elements, keys become child elements, and lists become repeated elements. The output updates while you type. There is no Convert button, no upload step and no download gate. Your JSON never leaves the browser tab.
Press Sample and you get a small library record: a city, a name, an open flag, two books with IDs, some tags and a phone number set to null. The converter hands back this:
| JSON (from the Sample) | XML the tool writes |
|---|---|
"@city": "London" on library | <library city="London"> |
"open": true | <open>true</open> |
"books": [ {…}, {…} ] | two <books> elements side by side |
"@id": "b1" inside the first book | <books id="b1"> |
"tags": ["sci-fi", "classic"] | <tags>sci-fi</tags><tags>classic</tags> |
"phone": null | <phone/> |
Under the result, a line reads 12 elements, 3 attributes. A note adds that one empty array wrote nothing. That's the second book's "tags": [], which has no items to repeat. The note is there so a missing element is never a mystery.
When you'll convert JSON to XML
Most new APIs speak JSON. A lot of the world still runs on XML. The gap between the two is where this tool lives.
Feeding an older system. Plenty of payment gateways, shipping carriers, government filing portals and bank interfaces take XML and nothing else. Your data sits in JSON because that's what your code produces. Paste it in and see what the XML looks like before you write the mapping code.
Writing a SOAP request or a config file by hand. Some build tools, feeds and enterprise configs are XML. Sketching the structure as JSON is faster. Converting it once gives you a starting file to edit.
Rebuilding XML from JSON. Something upstream turned XML into JSON, and now something downstream wants XML again. The converter follows the @-for-attributes and #text-for-text convention that Json.NET uses, and it reads Json.NET's other markers too. A top-level ?xml key is dropped, because the declaration comes from the XML declaration switch. #cdata-section becomes a CDATA section and #comment becomes a comment. Other libraries mark things their own way. xml2js puts attributes under a $ key and an element's text under _. fast-xml-parser prefixes attributes with @_ unless you set attributeNamePrefix to @. Rename those keys to @name and #text first, or they come out as child elements. Whichever library made the JSON, diff the result against the original XML before you rely on it. XML carries things no JSON shape records, like namespace declarations, entity references and the exact whitespace between elements.
Checking an ID survived. A standard JSON parser stores every number as a 64-bit float. A 20-digit order number comes out rounded, and nothing warns you. This tool shows you the digits in XML exactly as they were typed.
It works the same for a freelancer wiring up one client's invoicing feed, a student learning what an XML attribute is, and an engineer at a large company who needs the answer in thirty seconds and doesn't want to open a ticket for it.
How the conversion works
JSON and XML don't map one-to-one, so every converter has to make choices. These are the ones this one makes.
| JSON | Becomes in XML |
|---|---|
| An object | An element. Each key becomes a child element, in the same order. |
| An array under a key | That key's element, repeated once per item. No wrapper. |
| An array with no key (top level, or inside another array) | One <item> element per entry. |
A key starting with @, holding text, a number, true, false or null | An attribute on the parent element. |
A #text key | The element's own text. |
A #cdata-section or #comment key | A CDATA section, or an XML comment. |
A top-level ?xml key (Json.NET's record of the declaration) | Nothing. The declaration comes from the switch. |
null | A self-closing element: <phone/> |
"" (empty string) | An open-close pair: <phone></phone> |
| A number | The exact digits you typed. 1.500 stays 1.500. |
Choosing the root element
XML needs exactly one root element. JSON doesn't. When the Root element box is empty, the tool decides. If the document is an object with one key, and that key isn't holding a list, the key becomes the root. So {"note": {…}} becomes <note>…</note>. Anything else gets wrapped in <root>. Type a name in the box and the whole document is always wrapped in that element instead.
Escaping
Ampersands and angle brackets are escaped everywhere, and double quotes are escaped inside attributes. So {"show": {"@title": "Tom & \"Jerry\"", "#text": "a < b && c > d"}} becomes <show title="Tom & "Jerry"">a < b && c > d</show>. Any XML parser reads that back as the original text.
Examples: what goes in, what comes out
Every row below is what the JSON to XML converter actually returns, with the XML declaration off and the One line indent picked, so each result fits in the table.
| JSON you paste | XML you get |
|---|---|
{"a": 1, "b": 2} | <root><a>1</a><b>2</b></root> |
{"tags": ["a", "b"]} | <root><tags>a</tags><tags>b</tags></root> |
{"p": {"#text": "Hello ", "b": "world"}} | <p>Hello <b>world</b></p> |
{"m": [[1, 2], [3]]} | <root><m><item>1</item><item>2</item></m><m><item>3</item></m></root> |
{"order": {"id": 12345678901234567890, "total": 1.500}} | <order><id>12345678901234567890</id><total>1.500</total></order> |
{"cart": {"items": [], "total": 0}} | <cart><total>0</total></cart> plus a note about the empty array |
"hello" | <root>hello</root> |
The mixed-content row is worth a second look. When an element has both its own text and children, the tool stops indenting inside it, even with 2 or 4 spaces picked. Indentation there would add spaces to the text itself, and Hello world would stop being Hello world.
The order row is the one a standard parser gets wrong. Run that JSON through JSON.parse and the ID comes back as 12345678901234567000, and the total as 1.5. The ID looks right and isn't. This tool reads the digits as text and copies them straight into the XML.
When the result isn't what you expected
If the output box stays empty and a red message appears, the JSON didn't parse. Start there. The message names the line and the column, then the usual fix. These are the ones you'll see most:
| What's in your JSON | What the tool tells you |
|---|---|
| A comma after the last item | trailing comma before }. JSON doesn't allow a comma after the last item — delete it. |
{'a': 1} | keys need double quotes, not single quotes. |
{name: "Ada"} | the key `name` needs double quotes around it — JSON keys are always strings. |
A // comment | JSON has no comments. Delete the // or /* */ part and try again. |
{"ok": True} | True isn't JSON — it's written true, false or null, in lowercase. |
{"zip": 02134} | 02134 isn't a valid JSON number — numbers can't start with 0. Drop the leading zero, or quote it if it's a code like a ZIP. |
| Two objects pasted one after another | there's more after the JSON value ended. If you have several objects, put them inside [ ] with commas between them. |
For an unclosed bracket, the message points at where the bracket was opened, not where the file happened to end. the [ opened on line 2 is never closed with ]. That's the line you actually need to look at.
Keys that changed name
If the XML came out but some element names look odd, check the notes under the result. XML names can't start with a digit or hold spaces and most punctuation. So first name becomes first_name, 2fa becomes _2fa, and price ($) becomes price____. Keys in Japanese, Arabic or Cyrillic stay exactly as written, and xlink:href keeps its colon because that's how namespaces are spelled. The tool doesn't declare the prefix for you, though, and a namespace-aware parser rejects a prefix nobody declared. Add the declaration as an @ key on the element or an ancestor: {"a": {"@xmlns:xlink": "http://www.w3.org/1999/xlink", "@xlink:href": "#top"}} becomes <a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#top"/>.
Characters XML can't hold
A few control characters, like the bell character U+0007, are legal inside a JSON string and illegal anywhere in XML 1.0, even escaped. The tool swaps each one for the replacement character � and counts them in a note. The output stays well-formed, and you know exactly how many were touched.
Tips for clean XML
- Pick One line for machines, 2 or 4 spaces for people. One line writes the whole document with no whitespace between elements. That's the right choice when the XML goes into a request body or a single database column.
- Turn off the XML declaration when you're nesting. If the result is going inside another XML document, a second
<?xml …?>line in the middle makes the outer file invalid. - Name the root yourself when a system expects one. If the receiving side wants
<Request>, type Request in the Root element box. A name that isn't legal XML is refused with a suggestion: my root gets Try my_root. - Want a list wrapper? Put it in the JSON.
{"books": {"book": [ … ]}}writes one<books>holding a<book>per item. The tool never invents a wrapper you didn't ask for.
JSON-LD and schema.org data: keys like @context and @type are data there, not attribute markers. With attributes on, they land as context="…" and type="…" on the root. Switch @keys become attributes off and they come out as elements instead, renamed to <_context> and <_type> because an XML name can't start with @. The note lists both renames.
A converter, not a contract
Turning JSON into XML online usually goes one of two ways. You get a Convert button buried in ads, with a sign-up box between you and the download. Or the conversion is one connector inside an integration platform that bills per seat, runs on an annual contract, and now bundles an AI assistant into the renewal. Swapping curly brackets for angle brackets is not a subscription-sized problem.
So this is one page. Paste up to 5 MB of text and copy the result. There's no account, no trial clock, and no second tier where the real tool lives. This is the full tool.
If the JSON is a mess before you start, run it through the JSON formatter first. A missing comma is much easier to spot in indented JSON. Comparing two API responses before converting one? The JSON diff shows what changed. Heading somewhere other than XML? The JSON to YAML converter handles config files, and JSON to CSV flattens a list of records into spreadsheet rows. If your JSON is sitting inside a string full of escaped quotes, the JSON escape and unescape tool turns it back into something this converter can read.
Frequently asked questions
Can I convert XML back to JSON here?
Not on this page. It does one job, and that job is JSON to XML. The two directions aren't mirror images, either. XML carries namespaces, comments in odd places and whitespace that a JSON shape can't always record, so treat any trip from XML to JSON and back as something to diff, not something to trust.
Why is my root element called <root>?
Because your JSON had more than one top-level key, was a list, was a single key holding a list, or wasn't an object at all. XML allows exactly one root element, so the tool wraps the document in <root>. Type any legal name into the Root element box to change it. The name you type always wraps the whole document.
Does the XML keep the order of my keys?
Yes. Elements appear in the same order as the keys in your JSON. That matters because many XML schemas require elements in a fixed sequence. If a system complains about element order, reorder the keys in the JSON and the XML follows.
Will the output pass my XSD schema?
It's always well-formed XML 1.0. One catch: if your keys use a namespace prefix like dc: or og:, declare it with an @xmlns:dc key, or namespace-aware parsers will stop at the unbound prefix. Whether it's valid against a particular schema depends on your data: the right element names, the right order, the right attributes. The JSON to XML converter doesn't know your schema, so it writes exactly what your JSON describes and nothing more. Rename keys and add @ keys in the JSON until the shape matches.
Does it use CDATA sections?
Only when you ask for one. Ordinary text is escaped instead, so & becomes & and < becomes <. To an XML parser, escaped text and a CDATA section read back as the same string. Escaping also works inside attributes, where CDATA isn't allowed. To get a CDATA section, use a #cdata-section key the way Json.NET writes one: {"a": {"#cdata-section": "x < y"}} becomes <a><![CDATA[x < y]]></a>. A ]]> inside the text would end the section early, so the tool splits it across two sections and the text still reads back exactly.
Why not just ask an AI chatbot to convert it?
You can, and for ten lines it often works. But a language model writes the XML one token at a time, and nothing checks the result against your input. It can drop a digit from an ID, skip a repeated element or tidy up a key name, and the output still looks plausible. This converter follows fixed rules. The same JSON always gives the same XML, and every rename or dropped empty array shows up in a note.
Does it work on a phone?
Yes. It's the same tool on any screen, and nothing needs installing. Pasting a large API response on a phone is fiddly, but the conversion runs just as fast. Press Copy when you're done and the whole XML document goes to your clipboard.