JSON to XML Converter
Convert JSON to XML in your browser: root element, attributes, array style, valid names and CDATA.
A JSON to XML converter turns JSON data into an XML document that systems built around XML can read, such as SOAP services, older integrations, feeds and enterprise tools. JSON has objects, arrays, strings, numbers, booleans and null, while XML has elements, attributes and text, so the conversion needs rules. This page applies one fixed set of rules, shows each of them with an input and the exact output, and lets you change the choices that matter: the root element, how arrays are written, what happens to null, which key prefix marks an attribute, how invalid element names are handled, whether text with markup characters goes into CDATA sections, and the indentation.
How do I convert JSON to XML?
Paste your JSON into the box, pick a .json file, or load the sample. The XML appears on the right as soon as the JSON is valid, with the number of elements and the size. Change an option to update it, then copy the result or download it as a .xml file.
- Give it the JSON: paste it, drop a file on the upload box, or arrive from the JSON Formatter with a document it passed along. Invalid JSON is reported with its line and column.
- Pick the shape: choose the root element name, whether arrays repeat the parent element or wrap their items in an item element, and what to do with null.
- Mark attributes: any key that begins with the attribute prefix is written as an attribute of its element, and the text key becomes the element's text.
- Finish: decide on the XML declaration, the indentation and CDATA, then copy or download.
The XML to JSON converter reads what this page writes, and the JSON to YAML converter covers the other common configuration format.
How are JSON values mapped to XML elements?
Every key of a JSON object becomes a child element with the same name, in the order the keys appear. The rest follows from the type of the value, as the table shows.
| In the JSON | In the XML |
|---|---|
| An object key with a text, number or boolean value | A child element holding that value as text, for example <price>9.99</price> |
| An array under a key (default) | The element is repeated once per item: "items": ["pen", "ink"] gives two <items> elements |
| An array with Wrap items in <item> | One element named after the key, holding an <item> element for each entry |
| A top-level array | The root element holding an <item> element for each entry, because a document can have only one root |
A key that starts with @_ | An attribute of the element. The prefix can be changed to @ or _ |
The key #text | The text of the element, written before its child elements. The key name can be changed |
null | An empty element such as <note/>, or no element at all with Leave the element out |
| An empty object | An empty element |
| An empty array | Nothing when the parent element is repeated, an empty element when items are wrapped |
| A number | Text, copied exactly as it was typed in the JSON, so 12345678901234567890, 1.50 and 1e-7 are unchanged |
A string with &, < or > | The characters are escaped as &, < and >, or the text is placed in a CDATA section if you ask for that |
| An object with a single top-level key | That key becomes the root element, otherwise the keys become children of the root element whose name you set (root by default) |
Worked example: attributes, repeated elements and escaping
The catalog below uses the attribute prefix for updated and id, has an array of two books, a title with an ampersand, a title with angle brackets and a null note. Because the document has a single top-level key, catalog becomes the root element.
JSON input
{
"catalog": {
"@_updated": "2026-10-20",
"book": [
{
"@_id": "b1",
"title": "Dune & Co",
"price": 9.99,
"inStock": true
},
{
"@_id": "b2",
"title": "A <short> story",
"price": 12,
"inStock": false,
"note": null
}
]
}
}
XML output
<?xml version="1.0" encoding="UTF-8"?>
<catalog updated="2026-10-20">
<book id="b1">
<title>Dune & Co</title>
<price>9.99</price>
<inStock>true</inStock>
</book>
<book id="b2">
<title>A <short> story</title>
<price>12</price>
<inStock>false</inStock>
<note/>
</book>
</catalog>
The second book keeps its note as an empty element. Booleans and numbers become plain text, and the ampersand and the angle brackets are escaped, so the result is well-formed whatever the strings contain.
Worked example: two ways to write an array
XML has no array type. The two usual conventions are repeating the element and wrapping the entries in a container, and both are available here.
JSON input
{
"order": {
"id": 7,
"items": [
"pen",
"ink"
]
}
}
Arrays repeat the parent element (default)
<?xml version="1.0" encoding="UTF-8"?> <order> <id>7</id> <items>pen</items> <items>ink</items> </order>
Wrap items in <item>
<?xml version="1.0" encoding="UTF-8"?>
<order>
<id>7</id>
<items>
<item>pen</item>
<item>ink</item>
</items>
</order>
Repeating the element reads naturally and is what most XML-based APIs expect. Wrapping the entries keeps an array with one entry, or with none, recognisable as an array when the XML is read back, which the repeated form cannot do.
Worked example: keys that cannot be element names
An XML element name must start with a letter or an underscore and cannot contain spaces or most punctuation, but a JSON key can be any text. Here are five keys of which only two are valid names as they stand.
JSON input
{
"first name": "Ada",
"1st": "gold",
"a:b": "colon",
"ok": "fine",
"é": "accent"
}
XML output, declaration off
<root> <first_name>Ada</first_name> <_1st>gold</_1st> <a_b>colon</a_b> <ok>fine</ok> <é>accent</é> </root>
With Sanitize names on, each illegal character is replaced by an underscore and a name that starts with a digit gets an underscore in front, which is why 1st became _1st. A colon is replaced too, because a name with a colon is a namespace prefix and the converter never invents the namespace declaration it would need. The accented letter is a valid name and is kept. If you turn Sanitize names off, the converter refuses to write broken XML and says why:
Message with Sanitize names off
The key "first name" at /first name is not a valid XML element name. Turn on "Sanitize names" or rename the key.
Worked example: text with markup characters and CDATA
An element that has both an attribute and text is written with the text key. The text contains a tag-like word and an ampersand, so it has to be escaped, or wrapped in a CDATA section.
JSON input
{
"note": {
"@_lang": "en",
"#text": "Use <b> tags & ampersands"
}
}
Escaped (default)
<note lang="en">Use <b> tags & ampersands</note>
With CDATA for text containing < or &
<note lang="en"><![CDATA[Use <b> tags & ampersands]]></note>
CDATA is only used for text that contains a less-than sign or an ampersand, and a text that already contains the sequence that ends a CDATA section is split safely.
Worked example: exact numbers, null and empty arrays
This reading has a 20-digit id, a price written with a trailing zero, an exponent, a negative zero, a null and an empty array.
JSON input
{
"reading": {
"id": 12345678901234567890,
"price": 1.50,
"tiny": 1e-7,
"neg": -0,
"none": null,
"tags": []
}
}
Default options, declaration off
<reading> <id>12345678901234567890</id> <price>1.50</price> <tiny>1e-7</tiny> <neg>-0</neg> <none/> </reading>
Numbers are text in XML, so they are copied character for character, and nothing is rounded or rewritten in another notation. The empty array disappears, because repeating the parent element zero times writes nothing, and the converter tells you when that happens. Switching to Leave the element out for null and to wrapped array items gives this instead:
Leave the element out for null, wrap items in <item>
<reading> <id>12345678901234567890</id> <price>1.50</price> <tiny>1e-7</tiny> <neg>-0</neg> <tags/> </reading>
Root element, indentation and the declaration
A top-level array or a document with several top-level keys needs a root element. The array case is always wrapped in item elements, and the name comes from the Root element name field, which is root unless you change it.
JSON input, a top-level array
[
{
"id": 1
},
{
"id": 2
}
]
XML output, declaration off
<root>
<item>
<id>1</id>
</item>
<item>
<id>2</id>
</item>
</root>
Root element name: rows
<rows>
<item>
<id>1</id>
</item>
<item>
<id>2</id>
</item>
</rows>
The XML declaration, which states the version and the UTF-8 encoding, is written unless you switch it off. Indentation can be two spaces, four spaces, a tab or none at all. The same small document looks like this minified, with a tab, and with the root name set to data while the single-key shortcut is off:
Indent: none (minified)
<root><a>1</a><b><c>1</c><c>2</c></b></root>
Indent: tab
<root> <a>1</a> <b> <c>1</c> <c>2</c> </b> </root>
Root name data, single top-level key shortcut off
<data>
<a>1</a>
<b>
<c>1</c>
<c>2</c>
</b>
</data>
What does the XML to JSON converter bring back?
XML carries no types, so a round trip returns structure rather than types. We converted a set of documents with this page and read the XML back with the XML to JSON converter with Type inference on. These came back exactly: nested objects, repeated elements, special characters and Unicode text, strings with leading zeros, empty strings, and numbers and booleans. These did not:
| In the JSON | After XML to JSON |
|---|---|
| A null value | An empty string, unless you choose null for empty elements there |
| An array with one item | The item itself, unless you choose Always arrays there |
| A string that looks like a number or a boolean, such as "123" or "true" | A number or a boolean, because type inference cannot tell them apart |
| Text with spaces at the start or end | The text without them, while Trim whitespace is on |
| Several top-level keys | The same keys inside an extra root key |
| Wrapped array items | An object with an item key that holds the list |
| A number with more than 15 digits, or a spelling such as 1.5e-7 | A string: the digits of a long number are kept, but 1.5e-7 comes back as the text 1.5e-07 |
| A key that had to be sanitized | The sanitized name |
Common problems and how to fix them
The parser says the element name is invalid
This is the usual failure of naive converters, and it is the reason for Sanitize names. If you need the original text, keep it in a value instead of a key, for example a list of objects with a name field.
Should this be an attribute or an element?
Use an attribute for a short piece of metadata about the element, such as an id, a language or a unit, and an element for data that can repeat, nest or be long. Only text, numbers, booleans and null can be attributes here. If an attribute key holds an object or an array, the converter writes it as a child element and tells you.
The namespace prefix is not defined
No namespaces are generated. A key with a colon keeps its prefix only when an xmlns attribute for that prefix is present on the same element or one of its ancestors, as in "@_xmlns:p": "urn:example". Otherwise the colon is replaced, and the result is still well-formed.
Characters that XML cannot hold
Control characters other than tab, line feed and carriage return, the two non-characters U+FFFE and U+FFFF, and unpaired surrogates are not allowed in XML 1.0, even as escapes. They are removed, and the converter reports how many.
Key order surprises
JavaScript lists keys that look like whole numbers first, in ascending order, and the rest in the order they were written. The XML follows that order. A text key is written before the child elements of its element.
Should I use XML or JSON?
JSON is shorter, maps directly onto the objects and arrays of most programming languages, and is the default for web APIs. XML earns its place when you need attributes alongside content, mixed text and markup, namespaces, schema validation with XSD, transformation with XSLT, or when the other side of the integration only speaks XML. Converting is the practical bridge in both directions, and a second pass through the JSON compare tool shows whether anything changed on the way.
What are the limits of this converter?
Input of up to 100 MB is accepted, and anything above about 1.5 MB is converted straight from the file in a background worker with a time limit that grows with its size, a Cancel button and a busy indicator. Nesting is limited to 1,000 levels. The converter does not write comments, processing instructions, DOCTYPE declarations, schemas or namespace declarations of its own, and it writes text first and elements after it inside an element. Output beyond about 8 million characters cannot be copied to the clipboard and has to be downloaded.
Related tools
Frequently Asked Questions
How do I convert JSON to XML?
Paste your JSON, choose a .json file or load the sample. The XML appears as soon as the JSON is valid. Set the root element, the array style and the other options if you need them, then copy the XML or download it as a .xml file.
How are JSON arrays written in XML?
By default each array item becomes its own element with the name of the array's key, so "items": ["pen", "ink"] gives two items elements. Choose Wrap items in <item> to get one items element that holds an item element per entry. A top-level array is always wrapped in the root element with an item element per entry.
How do I make a JSON key an XML attribute?
Start the key with the attribute prefix, which is @_ by default and is the same prefix the XML to JSON converter writes. {"@_id": "7"} becomes id="7" on the element, and the text key #text becomes the text of that element. You can switch the prefix to @ or _ and rename the text key.
What happens to keys that are not valid XML names?
With Sanitize names on, which is the default, every illegal character becomes an underscore and a name that starts with a digit gets an underscore in front, so "first name" becomes first_name and "1st" becomes _1st. With it off the conversion stops with a message that names the key and its path.
Is my JSON uploaded anywhere?
No. The JSON is converted by a script running in your browser tab, and the converter does not send it anywhere. The page uses Google Analytics for anonymous usage counts, which never include your JSON, keys or file names.
Does converting the XML back to JSON give me the original?
For plain structures, yes: the XML to JSON converter returns the same keys, nesting and repeated elements. XML has no types, so turn on type inference there to get numbers and booleans back. Null becomes an empty string, a one-item array becomes a single value, and a text such as "123" becomes a number.
Built and maintained by Deepak Kumar.