JSON to XML Converter
Convert JSON to XML in your browser. Turn @ keys into attributes, choose repeated or item-wrapped arrays, set indentation, name the root, and toggle the declaration. Nothing is uploaded.
- Free, no account
- No watermark
- No usage limit
About the JSON to XML Converter
Paste JSON, get XML, and actually decide what the XML looks like. Most free converters hand you one fixed shape with no attributes and no say in how arrays translate, so the moment you needed real attributes or a different layout you were stuck hand-editing the result. This one lets you map @-prefixed keys to genuine XML attributes, choose how arrays come out, set the indentation, name the root, and switch the declaration on or off, all of it running in your browser. The JSON you paste is never uploaded, there's no server in the loop, and there's no account to create. The output updates as you type, ready to copy or save as a .xml file, and the special characters are escaped so the result is well-formed rather than the kind of "XML" that falls apart the second a parser meets an ampersand.
How to use
- Get your JSON into the box. Type it, paste it, drop a
.jsonfile onto the box, or use the Open .json file button. Any valid JSON works, a flat object, an array of records, or a deeply nested config. It just has to be real JSON: double-quoted keys, no trailing commas. - Set the output shape. Name the root element, pick whether
@-prefixed keys become attributes, choose repeated or wrapped arrays, and set the indentation. The controls sit right under the input and change the result instantly. - Read the XML as you go. There's no Convert button to hunt for. The XML appears the moment your JSON parses, and a small line count tells you how big it got.
- Copy or download. Hit Copy output for the clipboard, or Download .xml to save a file. Want the same setup next time? Use Share to copy a link that carries all your settings.
If the input isn't valid JSON, the output stays empty and a red message points at the likely cause, usually an unquoted key, a trailing comma, or a missing brace. Fix it and the XML comes right back. Nothing crashes on bad input, it just tells you what tripped.
Attributes are the part basic converters skip
A book element in real XML usually looks like <book id="b1" inStock="true">, with the id and the flag as attributes on the tag, which a naive converter cannot produce at all. It only knows how to nest, so it buries everything as child elements and you get <book><id>b1</id></book> instead. For a lot of SOAP payloads and config formats that expect attributes, that output is simply wrong, and you end up rewriting it by hand.
So this converter borrows a small, widely-used convention. Any key starting with your chosen marker (@ by default) is lifted onto the element as an attribute. A key named #text supplies the inner text instead. Take this:
{ "@id": "b1", "@inStock": "true", "title": "Refactoring", "author": "Fowler" }
With the root named book, that comes out as:
<book id="b1" inStock="true">
<title>Refactoring</title>
<author>Fowler</author>
</book>
The @id and @inStock keys landed as attributes, the ordinary keys stayed as child elements. And when an element needs both attributes and its own text, the #text key handles it. {"note":{"@lang":"en","#text":"Ship it"}} gives you <note lang="en">Ship it</note>, which is exactly the mixed shape a plain converter can't reach.
You can switch the marker to _ or $ if your data already uses @ for something else, or set it to off, in which case every key becomes a literal element and nothing is treated as an attribute. That off mode is the honest fallback for JSON where you want a straight one-to-one mapping.
One gotcha worth flagging: JSON-LD documents use @context, @type, and @id as real keys. With attribute mode on, those get pulled up as attributes, which may or may not be what you want. If you're converting JSON-LD and want those kept as elements, switch the marker to _ or turn attributes off.
Two ways to handle arrays
XML has no array type at all, no brackets and no list primitive, so an ordered collection has to be faked, and there are two accepted ways to do it, both of which are here.
The default is repeated elements: the same tag, once per item. {"tags":["news","release"]} becomes two sibling <tags> elements:
<tags>news</tags>
<tags>release</tags>
That's what RSS feeds, SOAP bodies, and most hand-written XML actually do, and it stays compact. The other option wraps each member in a generic <item> inside the key's element:
<tags>
<item>news</item>
<item>release</item>
</tags>
It's more verbose and adds a layer your data didn't have, but some schemas expect it, so it's there when you need it. Use whichever one your target actually reads. A top-level array is the one case with no choice to make, since there's no key to borrow the tag name from, each item just becomes an <item> under the root.
The stuff that quietly changes on the way over
JSON and XML don't line up cleanly, and being straight about the gaps is what keeps you from getting burned later. A few things shift:
Types flatten to text. A JSON number, boolean, and string all land as plain text between tags. 36 becomes <age>36</age>, and so does the string "36". In the XML, they're identical, because XML text carries no type. That's fine most of the time, you usually know what a field means, but it's a real loss if you plan to convert straight back.
Empty values become self-closing tags. That covers null, an empty string, an empty object and an empty array, all of which render as <middle/>. XML has no null, so a self-closing tag keeps the key present without inventing a value for it.
The syntax characters get escaped. XML reserves a handful for its own use, and left raw, one of them breaks the whole document. So &, < and > are converted automatically wherever they land, and inside an attribute the double quote goes with them, because that is the character ending the value:
Barnes & Noble -> Barnes & Noble
a <b> c -> a <b> c
A parser reads those right back as the originals, so Barnes & Noble survives the trip intact. Skip this step and a single ampersand in a company name makes the file fail to parse, which is the most common XML bug there is.
Line breaks and tabs get escaped too, and this is the one that catches people. A parser is required to squash a literal tab or line break inside an attribute down to a single space before your code ever sees the value. So a two line note written straight into an attribute comes back as one line with a space where the break was, and nothing warns you, because the file was perfectly well-formed. Writing the break as is what makes it survive, and that is what you get here. Carriage returns in element text have the same problem in a quieter way, since a lone \r normalizes to a plain line feed, so those come out as .
A few characters cannot go in at all. The old control codes below space, tab and line feed and carriage return excepted, are banned from XML outright, and no escape gets round it. Hand a parser a file with one in and it refuses the whole document rather than the one character. That is a real thing to hit if your JSON came out of a database column or a log line. Rather than give you a file that will not open, those characters are dropped and a note above the buttons says how many went, so you know before you ship it.
Messy keys get cleaned up. XML tag and attribute names can't contain spaces or start with a digit, but JSON keys can be anything. So "first name" becomes <first_name> and "1st" becomes <_1st>. Illegal characters turn into underscores and a leading digit gets an underscore in front, so the output stays well-formed no matter how rough the source keys are.
That last point ties into the round-trip question. Because this tool and our XML to JSON converter both read the same @ and #text convention, the structure of a document survives a there-and-back trip. Types are the exception, a converter reading XML back has to guess whether 36 was a number or a string, and it won't always guess right. So use JSON to XML when XML is your destination, a SOAP request, an RSS item, a config file, and keep the JSON as your source of truth if you need it back byte for byte.
Frequently asked questions
My array has only one item. Does that cause a problem downstream?
It can, and it's worth knowing before it bites. A one-item array like ["news"] produces a single <tags>news</tags>, which looks identical to a plain string value. Anything reading the XML back can't tell whether that was a list of one or a single value, because XML has no way to record the difference. If a downstream consumer strictly expects a list, use the wrap-in-item style so the array structure is always visible, or handle the single-versus-many case in the code that reads it.
Can the XML be turned back into the original JSON?
Structurally, yes, if you use our XML to JSON converter, which reads the same attribute and text convention. What doesn't come back cleanly is types. A ZIP code stored as "02134" can return as 2134 with the leading zero gone, since the XML only knows it as text. Treat the pair as great for reshaping data between the two formats, not as lossless storage for JSON you need returned exactly.
Why did some of my keys become attributes when I didn't want that?
Almost certainly because they start with the attribute marker. With the default @ marker on, any key like @id or @type is treated as an attribute. JSON-LD trips this a lot, since it uses @context and friends as ordinary keys. Switch the marker to _ or $, or set attributes to off, and every key goes back to being a plain element.
Does it generate a schema, namespaces, or a DOCTYPE?
No. It produces the XML body with an optional declaration line, and that's the scope on purpose. Namespaces, an XSD, or a DOCTYPE depend entirely on your target system, and guessing at them would produce output you'd have to unpick. Add those to the result yourself, or reach for a language library when you need that level of control over the document.
Is there a size cap, and is my data actually private?
There's no hard cap. The conversion happens on your own machine, so the practical limit is your browser and how much memory the file wants, and a few megabytes of JSON convert without trouble. Your JSON is parsed and rebuilt locally, never sent anywhere, never logged. Close the tab and it's gone, which makes this safe for internal payloads and API responses you'd never paste into a site that phones home.
What if I paste something that isn't valid JSON?
You get a plain inline error instead of a crash, and the output box stays empty. The message names the usual suspects, an unquoted key, a trailing comma, an unbalanced brace. A common trap: JSON needs double quotes around both keys and string values, so {name: 'ada'} fails while {"name": "ada"} works. Fix the input and the XML reappears on its own.