DEV Community

Chan Lebron
Chan Lebron

Posted on

JSON to XML: Why an Array Needs a Root Element

A JSON object with one key can still need an extra wrapper when that key holds an array. This is the small input I use to check that case:

{
  "item": [1, 2]
}
Enter fullscreen mode Exit fullscreen mode

The array contains two values. An XML document still needs one document element. The XML specification defines that requirement.

Two items, one document

For comparison, this deliberately broken document has two top-level elements:

<item>1</item>
<item>2</item>
Enter fullscreen mode Exit fullscreen mode

Those elements could be placed inside a larger document, but this text alone cannot be parsed as a well-formed XML document.

The tested converter adds a wrapper:

<root>
  <item>1</item>
  <item>2</item>
</root>
Enter fullscreen mode Exit fullscreen mode

The name root is a mapping choice. If a receiving API expects <items> or a namespace-qualified element, this output can parse successfully and still have the wrong shape for that API.

Here is a small check using Python's standard ElementTree parser:

import xml.etree.ElementTree as ET

array_xml = """<root>
  <item>1</item>
  <item>2</item>
</root>"""

root = ET.fromstring(array_xml)
assert root.tag == "root"
assert [child.tag for child in root] == ["item", "item"]
assert [child.text for child in root] == ["1", "2"]
print("array_root: passed")
Enter fullscreen mode Exit fullscreen mode

The parser checks the document structure. The assertions check the wrapper, element names, values and order expected by this particular mapping.

Add a nested object and text that needs escaping

My second fixture is a synthetic order:

{
  "order": {
    "@id": "00123",
    "customer": "Lee & Tom",
    "note": "<review>",
    "item": [
      { "sku": "A1" },
      { "sku": "B2" }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Here, the single non-array key order supplies the document element. This converter treats the @ prefix as an attribute convention. Its output is:

<order id="00123">
  <customer>Lee &amp; Tom</customer>
  <note>&lt;review&gt;</note>
  <item>
    <sku>A1</sku>
  </item>
  <item>
    <sku>B2</sku>
  </item>
</order>
Enter fullscreen mode Exit fullscreen mode

00123 stays a string in the source. The ampersand and angle brackets are escaped in the XML text, then decoded when a parser reads it:

order_xml = """<order id="00123">
  <customer>Lee &amp; Tom</customer>
  <note>&lt;review&gt;</note>
  <item>
    <sku>A1</sku>
  </item>
  <item>
    <sku>B2</sku>
  </item>
</order>"""

order = ET.fromstring(order_xml)
assert order.tag == "order"
assert order.attrib == {"id": "00123"}
assert order.findtext("customer") == "Lee & Tom"
assert order.findtext("note") == "<review>"
assert [item.findtext("sku") for item in order.findall("item")] == ["A1", "B2"]
print("nested_order: passed")
Enter fullscreen mode Exit fullscreen mode

Run the two Python blocks in order. The second uses the import from the first. Keep the XML escapes in the document; the parsed values show whether the text survived.

Agree on the mapping before sending the result

For an integration, I would check the receiving contract against three choices:

  • The required document element, including any namespace.
  • How arrays are represented: repeated elements or a collection wrapper.
  • Which fields become attributes, child elements or text.

@id is a converter convention, not a rule defined by JSON. The receiving system's schema or API documentation should determine the required structure. The assertions above verify these fixtures; they do not validate an XSD or prove that another API will accept them.

Both examples were run against the live DataToolForge JSON to XML converter on 2026-10-07, UTC+08:00, in a remote Chrome browser. The two Python blocks were also executed with Python 3.12.14. The screenshots show the actual tool results, and all inputs are synthetic.

You can paste either fixture into the converter and compare the result with the checks above. If your XML API needs a different array shape, which element names and wrapper does it require?

Disclosure: DataToolForge is my project. AI assistance was used to draft, test and edit this article.

Top comments (0)