Round-Trip Conversions: XML ➜ JSON with JsonWizard vs JsonFormatter
In the previous post we compared JsonWizard and JsonFormatter when converting JSON to XML. This time we’ll walk the road in reverse, converting XML back to JSON and analysing how each service copes with nested arrays, duplicate element names and the mandatory XML root node. As a senior Java engineer, I’ll keep the examples Java-centric so you can reproduce the findings in your own integration tests.
1. The baseline JSON
{
"name": "Belgium",
"capital": "Brussels",
"population": 11589623,
"area": 30528,
"currency": "Euro",
"languages": [
"Flemish",
"French",
"German"
]
}
We purposely added a languages
array because arrays are where many
XML ↔ JSON converters fail.
2. JsonFormatter’s XML output (problematic)
<?xml version="1.0" encoding="UTF-8"?>
<name>Belgium</name>
<capital>Brussels</capital>
<population>11589623</population>
<area>30528</area>
<currency>Euro</currency>
<languages>Flemish</languages>
<languages>French</languages>
<languages>German</languages>
Notice the absence of a single root element and the three duplicate
<languages>
tags. Both issues violate XML best-practice
and will confuse any round-trip converter.
Attempt #1 – XML ➜ JSON with JsonFormatter
[null]
JsonFormatter silently returns null
. No exception, no console log, just an empty
result. The duplicate element names and missing root apparently cause an internal failure that
never surfaces to the user—leaving you guessing what went wrong.
3. JsonWizard’s XML output (well-formed)
<root>
<name>Belgium</name>
<capital>Brussels</capital>
<population>11589623</population>
<area>30528</area>
<currency>Euro</currency>
<languages>
<language>Flemish</language>
<language>French</language>
<language>German</language>
</languages>
</root>
We now have a single <root>
wrapper and a properly nested
<languages>
section, paving the way for a safe
XML ➡ JSON round trip.
Attempt #2 – XML ➜ JSON with JsonWizard
{
"root": {
"name": "Belgium",
"capital": "Brussels",
"population": 11589623,
"area": 30528,
"currency": "Euro",
"languages": {
"language": [
"Flemish",
"French",
"German"
]
}
}
}
The result is not byte-for-byte identical to our original JSON, but it is semantically
equivalent: all values are preserved and the languages
list is intact.
The extra root
node appears because XML requires exactly one
top-level element. Converters therefore wrap everything in a dummy element and keep
it when returning to JSON to avoid information loss.
Why converters keep the <root> wrapper
XML’s well-formedness rule says a document must have a single root.
JSON, on the other hand, allows multiple properties at the top level.
If a converter simply discarded the wrapper on the way back, it would not know how to name
the top-level object when converting the JSON again or generating an XSD. Retaining
root
is therefore the safest reversible choice—though some libraries let you
strip it manually if you don’t need round-trip symmetry.
4. Verifying the round trip in Java
Below is a minimal JUnit 5 test that should pass when using JsonWizard’s output and
fail with JsonFormatter’s. The snippet relies on
com.fasterxml.jackson.dataformat:jackson-dataformat-xml v2.19.0
.
Note: Let's copy the above XML code into a file and save it as country.xml
for this test.
@Test
void shouldRoundTripCountry() throws Exception {
XmlMapper xml = new XmlMapper();
ObjectMapper json = new ObjectMapper();
String xmlText = Files.readString(Path.of("country.xml"));
Country country = xml.readValue(xmlText, Country.class);
String jsonOut = json.writerWithDefaultPrettyPrinter().writeValueAsString(country);
Country copy = json.readValue(jsonOut, Country.class);
assertEquals(country, copy);
}
@Data
static class Country {
public String name;
public String capital;
public String population;
public String area;
public String currency;
@JacksonXmlElementWrapper(localName = "languages")
@JacksonXmlProperty(localName = "language")
public List<String> languages;
}
5. Takeaways
JsonWizard succeeds because it keeps XML well-formed (single root, unique child names) and
exposes useful error messages. JsonFormatter stumbles on the same input, returning
null
without context—a risky black box for automated workflows.
Tested on June 1 2025 with the public versions of JSONFormatter and JsonWizard. Results may change as these services evolve.