XML to JSON, Explained: Turning Elements and Attributes Into Objects
JSON is the working language of most modern APIs, but a great deal of the world's data still arrives as XML: feeds, configuration files, legacy system exports, and document formats that predate JSON. Moving that data into a JSON-based system means converting it, and the conversion is not a mechanical mirror image of the reverse direction, because XML has features JSON lacks. The XML to JSON Converter converts simple XML into JSON in your browser, and knowing how its mapping choices work prevents the surprises that appear when the output reaches your code.
The basic mapping mirrors the JSON-to-XML one. An XML element becomes a JSON object, its child elements become keys, and its text content becomes the value. An element with only text, such as <title>Dune</title>, becomes the pair "title": "Dune". An element with children becomes a nested object, so <book><title>Dune</title></book> becomes {"book": {"title": "Dune"}}. The complications start with the two XML features JSON does not have: attributes and repeated elements. Attributes must go somewhere, and the common convention is to prefix them, for example with @, so <book id="42"> becomes {"book": {"@id": "42"}}, while another convention nests them under a special key such as @attributes. Repeated elements must become arrays, so three <tag> elements become a "tag" array of three strings, but a single <tag> element usually becomes a plain string, not a one-item array, and that inconsistency is the most common source of bugs in converted data: code that expects an array breaks on the document that happens to have only one item.
Mixed content is the case converters handle worst. Mixed content means an element containing both text and child elements, such as <p>The <em>quick</em> fox</p>, where the text is split around the child. JSON objects have no natural representation for interleaved text and elements, so converters typically either drop the loose text, concatenate it, or place it under a special key such as #text. None of these preserves the original exactly, and document-style XML, where mixed content is normal, converts far less cleanly than data-style XML, where elements contain either text or children but not both. If your XML is document-style, expect to clean the converted JSON by hand, or to use a converter designed for documents rather than data.
Types are lost in the same way as in the reverse conversion, and for the same reason: XML text is always text. The element <year>1965</year> becomes the string "1965", not the number 1965, and your code must convert it back if it needs a number. Boolean-like text such as true stays a string too, unless the converter has a type-inference option, which guesses types from the text and can guess wrong, turning a product code such as 0123 into the number 123 and losing the leading zero. Type inference is convenient and dangerous in equal measure, and for identifiers, codes and anything with leading zeros, it should be off. Namespaces add a final complication: an element such as <dc:title> carries a namespace prefix that the converter may keep, drop, or expand, and the receiving code must know which, or it will look for title and find dc:title instead.
A worked example you can follow
Convert this small XML catalogue:
<catalog>
<book id="42">
<title>Dune</title>
<year>1965</year>
</book>
<book id="43">
<title>Foundation</title>
<year>1951</year>
</book>
</catalog>
Count the structure first: 1 catalog element, 2 book elements, each with 1 title and 1 year, plus 2 id attributes, giving 1 + 2 + 4 = 7 elements and 2 attributes. Under the @ convention, the conversion gives:
{
"catalog": {
"book": [
{"@id": "42", "title": "Dune", "year": "1965"},
{"@id": "43", "title": "Foundation", "year": "1951"}
]
}
}
Check the mapping: the 2 repeated book elements became an array of 2 objects, which is correct, and the 2 attributes became @id keys, which is the convention to confirm with your code. The years arrived as strings, "1965" and "1951", so converting them to numbers is a separate step, and the difference between the two years, 1965 − 1951 = 14, is a calculation you do after conversion, not something the converter does for you. Now test the single-item case: remove the second book, and the same converter would produce "book" as a single object, not a one-item array, which is the inconsistency to guard against. Code that handles both shapes, by normalising a single object into a one-item array before processing, is immune to this, and writing that normalisation is a one-line fix that prevents a whole class of failures.
Verify the output by counting: the JSON should have the same number of books as the XML, the same titles, and the same attribute values, and a round trip back to XML should reproduce the original structure apart from the known type losses. If any count differs, the converter's handling of repeated elements or attributes is not what you assumed, and the assumption, not the data, is what needs fixing.
Common mistakes to avoid
The most common mistake is assuming a repeated element always becomes an array, and breaking on the single-item document. A second mistake is enabling type inference on identifiers and losing leading zeros. A third mistake is ignoring the attribute convention and looking for a plain key where the converter produced a prefixed one. A fourth mistake is converting document-style XML with mixed content and expecting a clean result. A fifth mistake is discarding the original XML after conversion, so a mapping error cannot be traced back to its source element.
XML to JSON conversion is a translation between two different models of data, not a format change, and translations have judgement calls. Learn your converter's conventions for attributes, repeated elements and mixed content, normalise the single-item case in your code, and keep the original XML until the JSON has been verified.
Frequently asked questions
Why is a single item not an array? Because most converters only create an array when repetition is observed, so a single element becomes a plain object or string; normalise it in your code.
What happens to attributes? They are usually prefixed, commonly with @, or nested under a special key, depending on the converter; check the convention before writing code against the output.
Does conversion preserve types? No. Everything arrives as a string, and numbers, booleans and dates must be converted back explicitly by your code.
For the reverse direction, see XML Formatter Guide, and pair this converter with the JSON to XML Converter and the XML Formatter when moving data between formats. Count the items, confirm the attribute convention, and normalise the single case.