DevToolLab

Free, fast, and powerful online tools for developers. Convert, format, and transform your data with ease.

© 2026 DevToolLab. All rights reserved.

Quick Links

ToolsBlogAbout
ContactFAQSupportTermsPrivacy
Upgrade to Pro (Ad-free)

Connect With Us

X

Have questions? Contact us

  1. Home
  2. /
  3. Blog
  4. /
  5. XML to JSON Conversion: Best Practices and Common Pitfalls
Back to all posts
Best Practices
10 min read

XML to JSON Conversion: Best Practices and Common Pitfalls

DevToolLab Team

DevToolLab Team

July 30, 2025 (Updated: March 8, 2026)

XML to JSON Conversion: Best Practices and Common Pitfalls

So you've got an XML payload and you need JSON. Sounds trivial until you realise XML and JSON have fundamentally different data models. XML has attributes, mixed content, namespaces, and implicit arrays. JSON has none of that baggage.

A naive string-replacement or a default xmltodict.parse() call will technically "work", but your resulting JSON will be inconsistent, break on edge cases, and drive you mad two weeks from now when the payload changes. Here's how to do it properly.

Why XML → JSON Isn't Just Find & Replace

Before we get into the code, understand the core structural mismatches:

XML ConceptJSON EquivalentProblem
Attributes (id="123")PropertiesJSON has no attribute concept
Single vs. multiple child elementsValue vs. ArraySingle item = string, multiple = array. Inconsistent
All text is a stringTyped values"true", "42" need real type inference
Namespaces (soap:Body)No equivalentNaming conflicts without careful handling

These mismatches cause the majority of bugs in XML-to-JSON pipelines. Let's tackle each one.

Challenge 1: Handling Attributes

XML attributes don't have a natural home in JSON. The most portable convention is the @ prefix:

<user id="123" role="admin">John Doe</user>

Converts to:

JSON
JSON Output
{
  "user": {
    "@id": "123",
    "@role": "admin",
    "#text": "John Doe"
  }
}

Using fast-xml-parser in Node.js:

JavaScript
import { XMLParser } from 'fast-xml-parser';

const parser = new XMLParser({
  ignoreAttributes: false,       // Don't throw away attributes!
  attributeNamePrefix: "@",      // Prefix attributes with @
  textNodeName: "#text",         // Name for the text content
  parseAttributeValue: true      // Auto-convert "123" → 123
});

const json = parser.parse(xmlString);

Don't set ignoreAttributes: true (which is the default in many libraries). You'll silently lose data like IDs, currency codes, and statuses that are often encoded as attributes in real-world XML.

Challenge 2: Arrays The Silent Killer

This is the one that bites everyone. XML doesn't differentiate between a collection with one item and a collection with many:

XML
<!-- One product -->
<products>
  <product>Widget A</product>
</products>

<!-- Multiple products -->
<products>
  <product>Widget A</product>
  <product>Widget B</product>
</products>

Without explicit configuration, most parsers turn these into different types:

JSON
// One item → string (bad!)
{ "products": { "product": "Widget A" } }

// Multiple items → array (what you'd expect)
{ "products": { "product": ["Widget A", "Widget B"] } }

This silently breaks your frontend code whenever a single-item edge case comes through. Your code does products.product.map(...) and suddenly crashes.

The Fix: Declare Your Array Tags

JavaScript
const parser = new XMLParser({
  ignoreAttributes: false,
  attributeNamePrefix: "@",
  isArray: (tagName) => {
    // These tags should ALWAYS be arrays, even with a single child
    const alwaysArrayTags = ['product', 'item', 'user', 'order', 'tag', 'role'];
    return alwaysArrayTags.includes(tagName);
  }
});

If you don't know the schema upfront, write a post-processing step that wraps known plural parent containers:

JavaScript
function normalizeArrays(obj, arrayKeys = ['products', 'items', 'users', 'orders']) {
  if (typeof obj !== 'object' || obj === null) return obj;

  for (const key of Object.keys(obj)) {
    if (arrayKeys.includes(key) && !Array.isArray(obj[key])) {
      obj[key] = [obj[key]]; // Force it to be an array
    }
    normalizeArrays(obj[key], arrayKeys);
  }
  return obj;
}

Challenge 3: Type Inference

XML treats everything as text. That means "true", "false", "42", and "3.14" are all strings unless you explicitly parse them.

The parseAttributeValue: true option in fast-xml-parser handles attributes, but element text content also needs care:

JavaScript
function parseTypedValue(value) {
  if (value === "true") return true;
  if (value === "false") return false;
  if (value === "null" || value === "") return null;

  const num = Number(value);
  if (!isNaN(num) && value.trim() !== "") return num;

  return value; // Fallback: keep as string
}

Or configure it directly in the parser:

JavaScript
const parser = new XMLParser({
  ignoreAttributes: false,
  attributeNamePrefix: "@",
  parseAttributeValue: true,
  parseTagValue: true,      // Also parses element text content
  trimValues: true          // Trim whitespace from values
});

Real-World Scenario: SOAP to REST Migration

One of the most common XML-to-JSON jobs is stripping SOAP envelope overhead when migrating to REST. Here's the pattern:

Incoming SOAP XML:

XML
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <User id="123">
      <Name>John Doe</Name>
      <Email>john@example.com</Email>
      <Roles>
        <Role>admin</Role>
        <Role>user</Role>
      </Roles>
    </User>
  </soap:Body>
</soap:Envelope>

Target REST JSON:

JSON
{
  "user": {
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "roles": ["admin", "user"]
  }
}

Here's a clean Node.js transformation:

JavaScript
import { XMLParser } from 'fast-xml-parser';

function soapToRest(soapXml) {
  const parser = new XMLParser({
    ignoreAttributes: false,
    attributeNamePrefix: "@",
    parseAttributeValue: true,
    parseTagValue: true,
    isArray: (name) => name === 'Role'
  });

  const parsed = parser.parse(soapXml);

  // Drill past the SOAP envelope wrapper
  const rawUser = parsed?.['soap:Envelope']?.['soap:Body']?.User;

  if (!rawUser) throw new Error("Could not locate User in SOAP body");

  return {
    user: {
      id: rawUser["@id"],
      name: rawUser.Name,
      email: rawUser.Email,
      roles: rawUser.Roles.Role.map(r => r.toLowerCase())
    }
  };
}

Real-World Scenario: XML Config to JSON Config

If you're migrating an app from XML-based config (Spring, Maven, etc.) to JSON, you need proper type inference or your app will fail to read port as a number:

XML Config:

XML
<database host="localhost" port="5432">
  <name>myapp</name>
  <ssl>true</ssl>
  <pool_size>10</pool_size>
</database>

Expected JSON Config:

JSON
{
  "database": {
    "host": "localhost",
    "port": 5432,
    "name": "myapp",
    "ssl": true,
    "pool_size": 10
  }
}

Tools and Libraries

JavaScript/Node.js

JavaScript
import { XMLParser } from 'fast-xml-parser';

const parser = new XMLParser({
  ignoreAttributes: false,
  parseAttributeValue: true,
  attributeNamePrefix: "@"
});

const json = parser.parse(xmlString);

Python

Python
import xmltodict
import json

with open('file.xml', 'r') as f:
    xml_content = f.read()
    json_data = xmltodict.parse(xml_content)
    print(json.dumps(json_data, indent=2))

Online Tools

Use our XML to JSON converter for quick conversions. It processes files locally for privacy.

Conclusion

Pick one attribute strategy and stick with it. The @ prefix works well. Handle arrays consistently always use arrays for collections. Implement type conversion to preserve numbers and booleans. Test with real data and handle errors gracefully.

For the reverse process, read our JSON to XML Conversion Guide. Check out Essential Developer Tools 2026 for more utilities.

xml
json
data-conversion
web-development
api

Related Posts

Top 5 Bitly Alternatives in 2026

As link management evolves in 2026, Bitly isn't the only player in town. Discover the best Bitly alternatives for branding, analytics, and tracking starting with DevToolLab's powerful URL Tracker.

By DevToolLab Team•March 22, 2026

Top 5 Local LLM Tools and Models in 2026

Stop paying massive API fees. Here is the ultimate engineering guide to the top 5 local inference engines and the groundbreaking open-weight models (like GPT-OSS and DeepSeek V3) redefining offline AI in 2026.

By DevToolLab Team•March 21, 2026

Best DNS for Gaming in 2026

Discover the best DNS servers for gaming in 2026 that can reduce latency, improve connection stability, and enhance your gaming experience with faster response times.

By DevToolLab Team•November 2, 2025