A partner sends over an integration spec. Their endpoint takes XML. Everything you have is JSON. On the surface it is a format swap — both describe structured data, both nest, both are text.
Then you hit the first real record. Your JSON has an array of five items, and XML has no concept of an array. It has a key starting with a digit, which XML forbids in element names. It has an id field that the spec says must be an attribute, not a child element — and JSON has no attributes at all.
Every JSON-to-XML converter quietly answers those questions for you. This guide walks through the four decisions it makes, shows the exact output for each, and points out which ones you can control — so the XML you hand over is the XML the other side expects.
Why JSON and XML Don't Line Up
JSON and XML both represent trees, so most of a conversion is straightforward: an object becomes an element, and its keys become child elements. The trouble is at the edges, where each format has features the other simply lacks.
XML has things JSON doesn't: attributes, namespaces, comments, and mixed content (text and elements side by side in the same node).
JSON has things XML doesn't: real arrays, and actual data types. JSON knows the difference between 28 and "28". In XML, everything between two tags is text.
Neither format is richer than the other — they are differently shaped. Converting means picking a convention for each gap, and those conventions are exactly what the next four sections cover.
Decision 1: The Root Element Name
Every XML document needs exactly one root element wrapping everything else. JSON has no such requirement, so the converter has to invent one.
CodBolt handles this with a small rule that saves you an edit: if your JSON has exactly one top-level key, that key becomes the root. This input:
{ "person": { "name": "John", "age": 28 } }produces a document rooted at <person>:
<?xml version="1.0" encoding="UTF-8"?>
<person>
<name>John</name>
<age>28</age>
</person>With more than one top-level key there is no obvious candidate, so a generic <root> wrapper is added instead:
{ "name": "John", "age": 28 }
<?xml version="1.0" encoding="UTF-8"?>
<root>
<name>John</name>
<age>28</age>
</root>So if the receiving system expects a specific root name such as <catalog> or <order>, wrap your JSON in a single key with that name before converting. You get the root you need without touching the output.
Decision 2: How Arrays Are Written
This is the biggest gap between the two formats. XML has no arrays — the closest thing is repeating an element, which is a convention rather than a feature. So a converter has to choose how a list is written, and CodBolt gives you both options through the Repeat Elements switch.
Repeat Elements on (the default)
Each item becomes its own element, all sharing the parent key's name. This is the idiomatic XML approach and what most schemas expect:
{ "user": { "name": "John", "skills": ["JavaScript", "Python"] } }
<user>
<name>John</name>
<skills>JavaScript</skills>
<skills>Python</skills>
</user>It works the same for arrays of objects, which is where it really pays off. A catalogue of books comes out looking exactly like hand-written XML:
<catalog>
<book>
<title>Dune</title>
<year>1965</year>
</book>
<book>
<title>Neuromancer</title>
<year>1984</year>
</book>
</catalog>Repeat Elements off
The list gets a wrapper element, and each entry becomes an <item> carrying its position:
<user>
<name>John</name>
<skills>
<item index="0">JavaScript</item>
<item index="1">Python</item>
</skills>
</user>The advantage here is that order is recorded explicitly in the index attribute, and the list has a clear container. Choose this when the consuming system cares about position, or when you want a single element you can point an XPath at.
Decision 3: Which Keys Become Attributes
XML can attach values directly to an element as attributes. JSON has no equivalent, so converters use a naming convention instead — and the widely adopted one is an @ prefix.
With Attributes Support on, any key starting with @ becomes an attribute rather than a child element:
{ "user": { "@id": "101", "name": "Alice" } }
<user id="101">
<name>Alice</name>
</user>There is a companion key for the other half of the problem. XML elements can hold attributes and text at the same time, which plain JSON cannot express. The #text key fills that gap:
{ "user": { "@id": "101", "#text": "Alice" } }
<user id="101">Alice</user>Together these two conventions let you produce essentially any XML shape a spec asks for, straight from JSON.
@ is treated as an ordinary character in a key name — and since XML does not allow it in element names, it is replaced with an underscore. Your @id becomes a child element called <_id> rather than an attribute. Both switches are on by default, so this only bites if you turned one off earlier.
Decision 4: Key Names and Empty Values
XML is far stricter than JSON about what a name may contain. A JSON key can be any string at all; an XML element name cannot contain spaces or most punctuation, and cannot begin with a digit.
Rather than failing, the converter repairs the names automatically — invalid characters become underscores, and a name that starts with a digit gains a leading underscore:
| JSON key | XML element | Why |
|---|---|---|
ok_key |
<ok_key> |
Already valid |
first name |
<first_name> |
Spaces aren't allowed |
1item |
<_1item> |
Can't start with a digit |
price($) |
<price___> |
One underscore per bad character |
Values are protected too. The five characters that would otherwise break the markup — &, <, >, " and ' — are escaped into entities, so text like a<b & c stays intact inside the element instead of corrupting the document.
Empty values are not all the same
Three JSON values look equally "empty" but produce three different results, which is worth knowing before you write a parser on the other side:
{ "note": null, "empty": "", "tags": [], "count": 0, "flag": false }
<?xml version="1.0" encoding="UTF-8"?>
<root>
<note/>
<empty></empty>
<count>0</count>
<flag>false</flag>
</root>nullbecomes a self-closing<note/>- An empty string becomes an open and close pair,
<empty></empty> - An empty array produces no element at all — there are no items to repeat, so nothing is written
- Zero and
falseare real values and come through normally, as<count>0</count>
Planning for the Round Trip
If your XML will eventually be converted back to JSON, two details are worth planning around. Both come from XML's design, not from any particular tool.
Types become text. "age": 28 becomes <age>28</age>, and there is nothing in that markup to say it was a number rather than the string "28". Anything reading the XML has to decide types for itself, usually from a schema.
A one-item list looks like a single value. With repeated elements, two skills produce two <skills> tags and one skill produces exactly one — identical in shape to a plain single field. A parser reading it back has no way to tell a one-item array from a scalar, which is a classic source of bugs that only appear once real data arrives.
If a faithful round trip matters, either supply a schema so the other side knows what to expect, or turn Repeat Elements off — the wrapper element makes a list unmistakably a list, even when it holds a single entry. When you need to come back the other way, XML to JSON handles the return journey.
How to Convert JSON to XML, Step by Step
- Open the JSON to XML converter.
- Paste your JSON into the left editor, or click Upload to load a
.jsonfile up to 100 MB. - Set the two switches. Attributes Support turns
@keys into attributes; Repeat Elements writes arrays as repeated tags. Both are on by default, which suits most cases. - Click Convert. The XML appears on the right, indented and with the declaration already in place.
- Check the root element first — it is the fastest way to confirm the output matches what the receiving system expects.
- Use Copy for the clipboard, or Download to save the result as a
data.xmlfile.
Click Sample to see the behaviour on a record that already contains an array and a nested object, which covers most of what you will meet in practice.
Everything runs in your browser — the JSON is never uploaded, which matters when you are converting real customer records for a partner integration.