Skip to content
FindTool

    XML to JSON

    Convert XML documents to JSON, with clear handling of attributes and text nodes.

    XML to JSON tool

    What this tool does

    XML and JSON do not have the same shape, so any conversion is a set of choices. This one makes those choices visible: attributes become keys with a prefix you pick, an element's text content lands under a key you pick, repeated sibling elements collapse into an array, and an element with nothing but text becomes a plain string rather than a wrapper object.

    Parsing is done by your browser's own DOMParser, the same engine that reads XML for fetch and XSLTProcessor, so entities, CDATA sections and namespace prefixes behave exactly as they would anywhere else on the platform. Nothing is uploaded.

    Common uses

    • Turning a SOAP response into something a JavaScript or Python client can index directly.
    • Reading an RSS or Atom feed in a script that expects JSON.
    • Converting a vendor's XML export into fixtures for tests.
    • Inspecting an SVG's structure — its attributes are where everything interesting lives.
    • Migrating an old XML config to a JSON-based one, field by field.

    A short example

    This fragment:

    <price currency="USD">184.25</price>
    <items><item>a</item><item>b</item></items>

    converts to:

    {
      "price": { "@currency": "USD", "#text": "184.25" },
      "items": { "item": ["a", "b"] }
    }

    The one-or-many problem

    This is the trap that breaks production code, and no converter can solve it alone. In XML, a list of one and a list of many look structurally identical — there is no bracket to say "this repeats". So <items><item>a</item></items> becomes {"item": "a"} while two items become {"item": ["a", "b"]}, and code written against a two-item response crashes the first time a feed has one entry.

    Only the schema knows which elements are repeatable. Two ways out: tick Always use arrays so every child element is wrapped, giving a shape that never changes with the data; or normalise on the consuming side with the equivalent of [].concat(value). The same ambiguity applies to text: an element that is empty one day and has content the next converts to "" and then to a string.

    Worth knowing

    There is no standard for this conversion. Badgerfish, Parker, GData and the fast-xml-parser convention all map XML differently, which is why the attribute prefix and text key are options here rather than fixed — match whatever your target library expects. Three further losses are unavoidable: JSON objects have no defined order, so interleaved elements of different names lose their document order; mixed content (text and elements as siblings) merges all the text into one key, so <p>see <b>this</b></p> cannot be reassembled; and namespace prefixes are kept verbatim as part of the key — soap:Body stays soap:Body — without resolving them to the URIs they are bound to. Converting back to XML from this JSON will not reproduce the original document byte for byte.

    Frequently asked questions

    Why is every number a string in the output?

    Because XML carries no types. <qty>3</qty> is one character of text; whether it is an integer lives in the XSD, which this converter does not read. Guessing would do damage — a document reference of 1E5 would silently become 100000. Cast the few fields you genuinely treat as numbers on your side, where you know which ones they are.

    SOAP responses sometimes use soap and sometimes S for the same thing. How do I write code against that?

    A prefix is chosen by whoever generated the document and means nothing on its own — only the URI it binds to is fixed, http://schemas.xmlsoap.org/soap/envelope/ for SOAP 1.1 and http://www.w3.org/2003/05/soap-envelope for 1.2. Because keys keep the prefix verbatim, code hardcoding soap:Body breaks against a server that writes S:Body. Match on the part after the colon, or rewrite prefixes before converting.

    Where did my CDATA section go?

    Into the text key, unwrapped. A CDATA block is only an escaping device, so the parser hands back the characters inside it and markup stored in a feed’s content:encoded element arrives as a string full of real HTML tags. That string is not safe to drop into a page — escape it at output with the HTML entity encoder, or render it through something that sanitises.