File2026-05-31·5 min read·By Sky Lu

How to Convert XML to JSON (and Back)

Convert XML to JSON for APIs and apps in seconds. Learn how, handle attributes and repeated tags, and reverse it back to XML. Free, no sign-up.

You have an XML file from an old system, an API that expects JSON, or a JSON response that needs to go back into an XML-based workflow. After reading this, you should be able to convert XML to JSON and JSON to XML without losing attributes, arrays, namespaces, or important text values.

The main challenge is not the conversion button or command. It is deciding how your XML structure should map to JSON, then checking the output so another app can actually read it.

Understand the XML-to-JSON mapping before you convert

XML and JSON do not describe data the same way. XML uses elements, attributes, text nodes, namespaces, and ordering. JSON uses objects, arrays, strings, numbers, booleans, and null values. A converter has to make choices, and those choices affect whether your output is usable.

Take this XML:

```xml Desk Lamp 39.99 lighting office ```

A common JSON conversion looks like this:

```json { "product": { "@id": "A-100", "@active": "true", "name": "Desk Lamp", "price": { "@currency": "USD", "#text": "39.99" }, "tags": { "tag": ["lighting", "office"] } } } ```

Notice the important details:

  • XML attributes often become keys with a prefix such as `@`.
  • Element text may become a plain string or a special key such as `#text`.
  • Repeated elements usually become arrays.
  • Values from XML often come through as strings, even if they look like numbers or booleans.
  • A single `` might convert to `"tag": "lighting"`, while two `` elements convert to `"tag": ["lighting", "office"]`.
  • That last point causes many real-world bugs. If your receiving application expects an array, make sure the converter always outputs an array for repeatable fields, even when there is only one item.

    For example, this XML:

    ```xml lighting ```

    may become:

    ```json { "tags": { "tag": "lighting" } } ```

    But your code may expect:

    ```json { "tags": { "tag": ["lighting"] } } ```

    Before converting a large file, convert a small sample that includes attributes, empty fields, repeated nodes, special characters, and nested elements. If that sample looks right, the full file is much more likely to behave.

    Convert XML to JSON using a practical workflow

    For one-off conversions, an online converter or editor is usually enough. For repeatable work, use a script so the same rules are applied every time.

    Step 1: Clean and validate the XML first

    Start by opening the XML in a code editor such as VS Code, Notepad++, Sublime Text, or any XML-aware editor. Check for the basics:

  • There must be one root element.
  • Every opening tag must have a closing tag.
  • Attribute values must be quoted.
  • Special characters must be escaped.
  • This is valid:

    ```xml Boston ```

    This is not:

    ```xml Boston ```

    The `&` character is a common problem. In XML text, use `&`. For `<`, use `<`. For `>`, use `>` when needed. Most JSON parsers are forgiving about regular text, but XML parsers are not.

    If the file came from an export, also check the encoding line:

    ```xml ```

    Use UTF-8 unless you have a specific reason not to. If names, accents, currency symbols, or non-English text look broken after conversion, encoding is the first thing to inspect.

    Step 2: Decide how to handle attributes

    Attributes are useful in XML, but JSON has no separate attribute concept. Most converters preserve them using a prefix.

    A clear convention is:

    ```json "@id": "123" ```

    or:

    ```json "_id": "123" ```

    Use one convention and keep it consistent. If you are sending JSON to an API, check the API’s expected field names. It may not accept `@id`; it may expect `id`.

    For internal use, keeping the prefix is safer because it preserves the difference between this:

    ```xml Book ```

    and this:

    ```xml 123 Book ```

    Both contain an ID, but they are not structurally identical.

    Step 3: Convert a representative sample

    Do not start with a 200 MB XML file. Copy 20 to 50 records into a test file and include edge cases:

  • One record with all fields filled
  • One record with missing optional fields
  • One record with repeated child elements
  • One record with only one child element in a repeatable group
  • One record with special characters such as `&`, quotes, and accented characters
  • One record with empty values
  • Example empty XML elements:

    ```xml ```

    These might convert to:

    ```json "middleName": "", "phone": null ```

    or:

    ```json "middleName": {}, "phone": {} ```

    Different converters handle them differently. If your API expects an empty string, do not accept `{}` without adjusting the output.

    Step 4: Format and inspect the JSON

    After conversion, pretty-print the JSON with 2-space indentation. Avoid tabs if the file will be reviewed by multiple people because spacing looks different across editors.

    Check these details:

  • Is the JSON valid?
  • Are repeated items arrays?
  • Did attributes survive?
  • Did text values inside attributed elements survive?
  • Are numbers supposed to be strings or numeric values?
  • Are booleans `"true"` strings or actual `true` booleans?
  • Did empty fields become null, empty strings, or empty objects?
  • If your next step is not XML but a spreadsheet or data feed, consider whether your data should be flattened first. For tabular data that starts in CSV, BestAIFinds has a simple CSV to JSON tool that is often cleaner than converting CSV to XML and then XML to JSON.

    Convert XML to JSON with code

    If you convert files regularly, scripting is safer than manual conversion because you can lock in the same rules.

    JavaScript example with fast-xml-parser

    Install:

    ```bash npm install fast-xml-parser ```

    Use this script:

    ```js const fs = require("fs"); const { XMLParser } = require("fast-xml-parser");

    const xml = fs.readFileSync("input.xml", "utf8");

    const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@", textNodeName: "#text", parseAttributeValue: false, parseTagValue: false, trimValues: true, isArray: (name, jpath) => { return ["products.product", "product.tags.tag", "orders.order"].includes(jpath); } });

    const json = parser.parse(xml);

    fs.writeFileSync("output.json", JSON.stringify(json, null, 2), "utf8"); ```

    The important settings:

  • `ignoreAttributes: false` keeps XML attributes.
  • `attributeNamePrefix: "@"` makes attributes easy to identify.
  • `textNodeName: "#text"` stores text in elements that also have attributes.
  • `parseAttributeValue: false` keeps attribute values as strings.
  • `parseTagValue: false` keeps tag values as strings.
  • Keeping values as strings is often safer during conversion. You can transform types afterward when you know which fields should be numbers, booleans, or dates. Otherwise, a value such as `"00123"` may become `123`, losing the leading zeros.

    The `isArray` option is especially useful. It lets you force known repeatable paths to arrays even when only one item appears.

    Python example with xmltodict

    Install:

    ```bash pip install xmltodict ```

    Script:

    ```python import json import xmltodict

    with open("input.xml", "r", encoding="utf-8") as f: xml_data = f.read()

    data = xmltodict.parse( xml_data, attr_prefix="@", cdata_key="#text", force_list=("product", "tag", "order") )

    with open("output.json", "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) ```

    Use `ensure_ascii=False` if you want characters like `é`, `ñ`, and `€` to remain readable instead of being escaped. The JSON is still valid either way, but readable output is easier to review.

    `force_list` is the setting that prevents one-item lists from becoming plain objects or strings. Add every repeatable element name that your data model uses.

    Convert JSON back to XML without surprises

    Going from JSON to XML is not always a perfect reverse operation. If the JSON was created from XML using a known convention, you can usually rebuild it. If the JSON came from an API with no XML history, you need to design the XML structure.

    Start with this JSON:

    ```json { "product": { "@id": "A-100", "@active": "true", "name": "Desk Lamp", "price": { "@currency": "USD", "#text": "39.99" }, "tags": { "tag": ["lighting", "office"] } } } ```

    The XML should become:

    ```xml Desk Lamp 39.99 lighting office ```

    JavaScript example with fast-xml-parser

    ```js const fs = require("fs"); const { XMLBuilder } = require("fast-xml-parser");

    const json = JSON.parse(fs.readFileSync("input.json", "utf8"));

    const builder = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: "@", textNodeName: "#text", format: true, indentBy: " ", suppressEmptyNode: false });

    const xml = builder.build(json);

    fs.writeFileSync("output.xml", xml, "utf8"); ```

    Use `format: true` for readable XML. Use `suppressEmptyNode: false` if your receiving system distinguishes between:

    ```xml ```

    and:

    ```xml ```

    Some systems treat them the same, but older importers sometimes do not.

    Python example with xmltodict

    ```python import json import xmltodict

    with open("input.json", "r", encoding="utf-8") as f: data = json.load(f)

    xml = xmltodict.unparse( data, pretty=True, indent=" ", full_document=True )

    with open("output.xml", "w", encoding="utf-8") as f: f.write(xml) ```

    This produces an XML declaration at the top because `full_document=True`:

    ```xml ```

    If you are pasting the XML into another document or SOAP body, you may need `full_document=False` to avoid having an XML declaration in the middle of another XML file.

    Common mistakes and how to fix them

    Mistake 1: Losing attributes

    If this XML:

    ```xml Rina ```

    becomes:

    ```json { "user": { "name": "Rina" } } ```

    your converter ignored attributes. Look for a setting named `ignoreAttributes`, `preserveAttributes`, or `attributes`. Set it so attributes are included. In code, choose a prefix such as `@`.

    Mistake 2: Arrays changing shape

    A file with two invoices may produce:

    ```json "invoice": [ { "id": "1" }, { "id": "2" } ] ```

    A file with one invoice may produce:

    ```json "invoice": { "id": "1" } ```

    This breaks code that loops over invoices. Force lists for repeatable elements. In Python `xmltodict`, use `force_list=("invoice",)`. In JavaScript `fast-xml-parser`, use `isArray`.

    Mistake 3: Accidentally converting IDs to numbers

    XML stores text. If your converter parses values automatically, this:

    ```xml 000456 ```

    may become:

    ```json "accountId": 456 ```

    That is not the same ID. Disable automatic value parsing during conversion, then convert only known numeric fields later. Product IDs, ZIP codes, account numbers, phone numbers, and SKU values should usually stay strings.

    Mistake 4: Mishandling namespaces

    XML namespaces look like this:

    ```xml A-100 ```

    Some converters keep prefixes in the JSON keys:

    ```json "soap:Envelope": { "soap:Body": { "m:GetPrice": { "m:Item": "A-100" } } } ```

    That is fine if you need to rebuild the same XML. If you only need the business data, you may want to strip namespaces and extract just:

    ```json { "item": "A-100" } ```

    Do not strip namespaces blindly when working with SOAP, finance, government, medical, or standards-based files. The receiving system may require the exact namespace URI and prefix structure.

    Mistake 5: Mixed content

    Mixed content means text and child elements are combined:

    ```xml

    Please read the terms carefully.

    ```

    This does not map neatly to ordinary JSON. A converter may produce something like:

    ```json { "p": { "#text": "Please read the carefully.", "strong": "terms" } } ```

    The original word order is partly lost unless the converter preserves node order. If your XML contains document-style content, such as articles, contracts, or formatted descriptions, test carefully. Basic XML-to-JSON conversion works best for data records, not rich text documents.

    Troubleshooting checklist for real files

    If the conversion fails immediately, open the XML and check the first error line. XML parsers usually stop at the first invalid character or unclosed tag. Fix that error, then run again. One broken ampersand near the top can hide many later issues.

    If the JSON output is valid but the importing app rejects it, compare field names exactly. JSON keys are case-sensitive. `"CustomerID"`, `"customerId"`, and `"customerID"` are three different keys.

    If the output file is huge, avoid pretty-printing for production. Pretty JSON is easier to read, but it adds whitespace. Use pretty formatting while testing, then compact JSON for transfer:

    ```js JSON.stringify(data) ```

    instead of:

    ```js JSON.stringify(data, null, 2) ```

    If dates change, keep them as strings during conversion. XML has no single required date format. You may see `2026-03-15`, `03/15/2026`, or `2026-03-15T14:30:00Z`. Convert dates only after you know the required target format.

    If characters look wrong, confirm every step uses UTF-8: the XML declaration, your editor, your script’s file read, and your script’s file write. In Python, always pass `encoding="utf-8"` when reading and writing these files.

    Practical wrap-up

    The safest way to convert XML to JSON is to preserve attributes, force repeatable elements into arrays, keep values as strings at first, and test with a small sample before processing the full file. Going back from JSON to XML works well if your JSON keeps clear markers for attributes and text nodes.

    If your source data is actually tabular rather than XML-based, try the BestAIFinds CSV to JSON tool first. It may give you cleaner JSON with fewer structural surprises.

    SL

    Sky Lu

    Solo developer behind BestAIFinds — 240+ free, no-signup file tools, most running entirely in your browser. More about me →