n8n XML Node
📄
Transform Node

n8n XML Node

Master the n8n XML node for converting between XML and JSON. Learn attribute handling, SOAP API integration, data feed parsing, and troubleshoot common conversion errors.

Legacy APIs, enterprise systems, and data feeds still speak XML while your workflows speak JSON. That SOAP service returning inventory data? XML. The RSS feed you need to parse? XML. The configuration file from your vendor’s system? Also XML. Every time you try to work with this data in n8n, you hit a wall because the workflow expects JSON.

The XML node bridges this gap. It converts XML to JSON for processing in your workflows and transforms JSON back to XML when you need to send data to XML-based systems. No coding required. Whether you’re integrating with a decades-old enterprise system or parsing modern data feeds, this node handles the translation.

The XML-JSON Bridge Challenge

XML and JSON represent data differently:

  • XML uses tags and attributes with strict hierarchy rules (XML specification)
  • JSON uses objects and arrays with flexible nesting
  • XML attributes have no direct JSON equivalent
  • XML namespaces add complexity that JSON avoids

Manual conversion is tedious and error-prone. Copy-pasting between online converters breaks automation. The XML node handles these structural differences automatically, mapping elements to properties and preserving the relationships in your data.

What You’ll Learn

  • When to use the XML node versus alternatives like Code node or Extract from File
  • Both conversion modes: XML to JSON and JSON to XML
  • All configuration options and what they actually do
  • How attribute keys and character keys work
  • Processing SOAP API responses in your workflows
  • Parsing RSS feeds and XML data files
  • Creating XML output for legacy system integration
  • Troubleshooting common conversion errors
  • Best practices for reliable XML processing

When to Use the XML Node

Before adding the node, confirm it’s the right tool. The XML node handles format conversion, not data extraction or complex transformations.

ScenarioUse XML Node?Why
Convert SOAP API response to JSONYesDirect XML string to JSON conversion
Parse RSS/Atom feed dataYesStandard XML feeds convert cleanly
Process XML configuration filesYesFirst extract text, then convert
Create XML for legacy API requestYesJSON to XML with proper formatting
Transform XML from HTTP RequestYesWorks directly with response data
Extract specific values from XMLMaybeConvert first, then use Edit Fields
Complex XML with namespacesYesNamespaces become JSON prefixes
Streaming large XML filesNoNode loads entire content to memory

Rule of thumb: Use the XML node when you need complete XML-to-JSON or JSON-to-XML conversion. For partial extraction from converted data, add an Edit Fields node afterward.

When Not to Use the XML Node

The XML node has specific limitations:

LimitationWhat HappensBetter Approach
Binary XML filesNode expects string inputUse Extract from File first (see binary data guide)
XPath queriesNo XPath supportConvert to JSON, then use expressions
XSLT transformationsNot supportedUse Code node with libraries
XML validationNo schema validationValidate externally before processing
Selective element parsingConverts entire documentConvert all, filter with Edit Fields
Very large XML files (>50MB)Memory constraintsProcess in chunks or use streaming tools

Understanding the Two Conversion Modes

The XML node operates in two distinct modes. Each direction has different options because the data structures have different characteristics.

XML to JSON

Converts XML markup into JSON objects. The node parses the XML structure and maps elements, attributes, and text content to JSON properties.

Input example:

<?xml version="1.0" encoding="UTF-8"?>
<order id="12345">
  <customer>John Doe</customer>
  <items>
    <item sku="ABC123">
      <name>Widget</name>
      <quantity>3</quantity>
      <price>29.99</price>
    </item>
  </items>
  <total currency="USD">89.97</total>
</order>

Output (with default settings):

{
  "order": {
    "$": {
      "id": "12345"
    },
    "customer": ["John Doe"],
    "items": [{
      "item": [{
        "$": { "sku": "ABC123" },
        "name": ["Widget"],
        "quantity": ["3"],
        "price": ["29.99"]
      }]
    }],
    "total": [{
      "$": { "currency": "USD" },
      "_": "89.97"
    }]
  }
}

Notice how:

  • Attributes appear under the $ key
  • Text content with attributes appears under the _ key
  • Elements become arrays by default (Explicit Array option)
  • The root element becomes a top-level property

Best for:

  • Processing SOAP API responses
  • Parsing RSS and Atom feeds
  • Converting XML data exports
  • Reading XML configuration files

JSON to XML

Converts JSON objects into valid XML documents. The node follows specific conventions to map JSON properties back to XML elements and attributes.

Input example:

{
  "order": {
    "$": {
      "id": "12345"
    },
    "customer": "John Doe",
    "item": {
      "$": { "sku": "ABC123" },
      "name": "Widget",
      "quantity": 3
    }
  }
}

Output:

<?xml version="1.0" encoding="UTF-8"?>
<order id="12345">
  <customer>John Doe</customer>
  <item sku="ABC123">
    <name>Widget</name>
    <quantity>3</quantity>
  </item>
</order>

Best for:

  • Creating SOAP request bodies
  • Generating XML for legacy APIs
  • Building XML configuration files
  • Formatting data for XML-based systems

Quick Comparison

AspectXML to JSONJSON to XML
Input formatXML stringJSON object
Output formatJSON objectXML string
Attribute handlingMaps to $ keyReads from $ key
Text with attributesMaps to _ keyReads from _ key
ArraysElements become arraysArrays become repeated elements
Primary useParsing API responsesCreating API requests

Your First XML Conversion

Let’s build a complete workflow that fetches XML data and converts it to JSON.

Step 1: Fetch XML Content

First, use the HTTP Request node to retrieve XML data:

  1. Add an HTTP Request node to your workflow
  2. Set Method to GET
  3. Enter a URL that returns XML (for testing, use a public RSS feed)
  4. Click Test step

The node returns XML content in the response body.

Step 2: Add the XML Node

  1. Add an XML node after HTTP Request
  2. Set Mode to “XML to JSON”
  3. For Property Name, enter the field containing your XML (typically data from HTTP Request)
  4. Leave other options at defaults for now

Step 3: Test the Conversion

Click Test step. The output shows your XML converted to a JSON structure:

{
  "rss": {
    "$": { "version": "2.0" },
    "channel": [{
      "title": ["Example Feed"],
      "link": ["https://example.com"],
      "item": [
        { "title": ["First Article"], "link": ["https://example.com/1"] },
        { "title": ["Second Article"], "link": ["https://example.com/2"] }
      ]
    }]
  }
}

Step 4: Access Converted Data

Use expressions in subsequent nodes to access the converted JSON:

// Access feed title
{{ $json.rss.channel[0].title[0] }}

// Access first item's title
{{ $json.rss.channel[0].item[0].title[0] }}

// Get all item titles
{{ $json.rss.channel[0].item.map(i => i.title[0]) }}

Notice the array notation [0] - this is because Explicit Array is enabled by default, wrapping elements in arrays.

Configuration Deep Dive

Understanding every option prevents unexpected conversion results.

Property Name

The field containing your data to convert.

For XML to JSON:

  • Enter the name of the property containing the XML string
  • Common values: data, body, xml, or the field from your previous node
  • If you’re testing with sample XML, you can paste it directly into a Set node first

For JSON to XML:

  • Enter the name of the property containing the JSON object
  • Usually references data from previous workflow nodes
  • The property value must be a valid JSON object, not a string

Attribute Key (Default: $)

Controls how XML attributes appear in JSON output (XML to JSON) or where the node looks for attributes (JSON to XML).

XML to JSON example:

With Attribute Key set to $, this XML:

<product id="123" active="true">Widget</product>

Becomes:

{
  "product": {
    "$": { "id": "123", "active": "true" },
    "_": "Widget"
  }
}

Accessing attributes in expressions:

{{ $json.product.$.id }}      // "123"
{{ $json.product.$.active }}  // "true"

JSON to XML example:

To create XML with attributes, structure your JSON with the $ key:

{
  "product": {
    "$": { "id": "123", "active": "true" },
    "name": "Widget"
  }
}

Output:

<product id="123" active="true">
  <name>Widget</name>
</product>

Character Key (Default: _)

Controls how text content appears when an element has both attributes and text.

XML to JSON example:

With Character Key set to _:

<price currency="USD">29.99</price>

Becomes:

{
  "price": {
    "$": { "currency": "USD" },
    "_": "29.99"
  }
}

Accessing in expressions:

{{ $json.price._ }}           // "29.99"
{{ $json.price.$.currency }}  // "USD"

JSON to XML example:

To create an element with both attributes and text content:

{
  "price": {
    "$": { "currency": "USD" },
    "_": "29.99"
  }
}

Output:

<price currency="USD">29.99</price>

XML to JSON Options

These options only appear when converting XML to JSON:

OptionDefaultEffect
Explicit ArrayOnWraps all child elements in arrays
Explicit RootOnIncludes root element in output
Ignore AttributesOffSkips XML attributes entirely
Merge AttributesOffMerges attributes with child properties
NormalizeOffTrims whitespace in text nodes
Normalize TagsOffConverts all tag names to lowercase

Explicit Array explained:

With Explicit Array ON (default):

{ "items": { "item": [{ "name": ["Widget"] }] } }

With Explicit Array OFF:

{ "items": { "item": { "name": "Widget" } } }

The array format is safer because it handles both single and multiple elements consistently. Without it, a single <item> becomes an object but multiple <item> elements become an array, making your expressions unpredictable.

Explicit Root explained:

With Explicit Root ON (default):

{ "order": { "customer": ["John"] } }

With Explicit Root OFF:

{ "customer": ["John"] }

Turn this off when you only need the root element’s children and want cleaner access paths.

Merge Attributes explained:

With Merge Attributes OFF (default):

{ "product": { "$": { "id": "123" }, "name": ["Widget"] } }

With Merge Attributes ON:

{ "product": { "id": "123", "name": ["Widget"] } }

Merging makes access simpler ($json.product.id instead of $json.product.$.id) but can cause conflicts if an attribute has the same name as a child element.

JSON to XML Options

OptionDefaultEffect
HeadlessOffOmits XML declaration header
Root Name-Custom root element name
CdataOffWraps text in CDATA sections (use when text contains <, >, or &)
Attribute Key$Key for attribute values
Character Key_Key for text content

Headless option:

With Headless OFF (default):

<?xml version="1.0" encoding="UTF-8"?>
<order>...</order>

With Headless ON:

<order>...</order>

Some APIs don’t accept the XML declaration. Enable Headless for those cases.

Root Name option:

When your JSON doesn’t have a single root property, specify one:

Input:

{ "name": "Widget", "price": 29.99 }

With Root Name set to product:

<product>
  <name>Widget</name>
  <price>29.99</price>
</product>

Working with XML Attributes

Attributes are the trickiest part of XML-JSON conversion. Here’s how to handle common patterns.

Reading Attributes from XML

After converting XML to JSON, attributes live under the $ key:

<user id="42" status="active" role="admin">
  <name>John Doe</name>
  <email>[email protected]</email>
</user>

Becomes:

{
  "user": {
    "$": {
      "id": "42",
      "status": "active",
      "role": "admin"
    },
    "name": ["John Doe"],
    "email": ["[email protected]"]
  }
}

Access patterns:

// Get user ID
{{ $json.user.$.id }}

// Get all attributes as object
{{ $json.user.$ }}

// Check status
{{ $json.user.$.status === 'active' }}

Creating Attributes in XML

To generate XML with attributes, structure your JSON properly:

{
  "invoice": {
    "$": {
      "number": "INV-2024-001",
      "date": "2024-01-15"
    },
    "customer": {
      "$": { "id": "CUST-123" },
      "_": "Acme Corp"
    },
    "total": {
      "$": { "currency": "USD" },
      "_": "1500.00"
    }
  }
}

Output:

<invoice number="INV-2024-001" date="2024-01-15">
  <customer id="CUST-123">Acme Corp</customer>
  <total currency="USD">1500.00</total>
</invoice>

Handling Namespaced XML

XML namespaces appear as prefixed tags:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:GetUserResponse xmlns:ns1="http://example.com/users">
      <ns1:User>John</ns1:User>
    </ns1:GetUserResponse>
  </soap:Body>
</soap:Envelope>

After conversion, namespace prefixes become part of the property names:

{
  "soap:Envelope": {
    "$": { "xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/" },
    "soap:Body": [{
      "ns1:GetUserResponse": [{
        "$": { "xmlns:ns1": "http://example.com/users" },
        "ns1:User": ["John"]
      }]
    }]
  }
}

Access with bracket notation for colons:

{{ $json['soap:Envelope']['soap:Body'][0]['ns1:GetUserResponse'][0]['ns1:User'][0] }}

For cleaner access, use a Code node to strip namespace prefixes after conversion.

Real-World Examples

Example 1: Converting SOAP API Responses

Scenario: Integrate with a legacy inventory system using SOAP.

Workflow:

Manual Trigger → HTTP Request (SOAP) → XML to JSON → Edit Fields → Output

HTTP Request configuration:

  • Method: POST
  • URL: https://legacy.example.com/soap/inventory
  • Headers: Content-Type: text/xml; charset=utf-8
  • Body (raw XML with SOAP envelope):
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetInventory xmlns="http://example.com/inventory">
      <sku>WIDGET-001</sku>
    </GetInventory>
  </soap:Body>
</soap:Envelope>

XML Node:

  • Mode: XML to JSON
  • Property Name: data
  • Explicit Array: On
  • Merge Attributes: On (for cleaner namespace handling)

Edit Fields (extract the data you need):

// Access the response data
{{ $json['soap:Envelope']['soap:Body'][0]['GetInventoryResponse'][0] }}

For more on working with HTTP requests, see our HTTP Request node guide.

Example 2: Parsing RSS Feeds

Scenario: Aggregate content from multiple RSS feeds for a newsletter.

Workflow:

Schedule Trigger → HTTP Request (RSS) → XML to JSON → Split Items → Format → Merge

HTTP Request:

  • URL: https://blog.example.com/feed.xml
  • Method: GET

XML Node:

  • Mode: XML to JSON
  • Property Name: data
  • Explicit Root: On

Code Node (extract articles):

const items = $input.all();
const feed = items[0].json;

// Navigate to the items in RSS structure
const channel = feed.rss.channel[0];
const articles = channel.item || [];

// Map to clean objects
return articles.map(article => ({
  json: {
    title: article.title?.[0] || '',
    link: article.link?.[0] || '',
    description: article.description?.[0] || '',
    pubDate: article.pubDate?.[0] || ''
  }
}));

Example 3: Creating XML for Legacy System

Scenario: Send order data to an ERP system that only accepts XML.

Workflow:

Webhook (order data) → Edit Fields (structure) → JSON to XML → HTTP Request (POST)

Edit Fields (structure data for XML):

{
  "order": {
    "$": {
      "id": "{{ $json.orderId }}",
      "date": "{{ $now.toISODate() }}"
    },
    "customer": {
      "$": { "id": "{{ $json.customerId }}" },
      "_": "{{ $json.customerName }}"
    },
    "items": {
      "item": "{{ $json.items.map(i => ({ '$': { sku: i.sku }, name: i.name, qty: i.quantity })) }}"
    },
    "total": {
      "$": { "currency": "USD" },
      "_": "{{ $json.total }}"
    }
  }
}

XML Node:

  • Mode: JSON to XML
  • Property Name: The field containing structured JSON
  • Headless: Off (include XML declaration)

HTTP Request:

  • Method: POST
  • URL: https://erp.example.com/api/orders
  • Content-Type: application/xml
  • Body: {{ $json.data }} (the XML output)

Example 4: Processing XML Data Feeds

Scenario: Parse a product catalog XML file received via email or FTP.

Workflow:

Trigger → Read Binary File → Extract from File → XML to JSON → Loop → Save to DB

Extract from File:

  • Operation: Text
  • This converts binary file to text string

XML Node:

  • Mode: XML to JSON
  • Property Name: data (from Extract from File)
  • Explicit Array: On
  • Normalize: On (clean up whitespace)

Processing loop:

// The converted JSON structure
const catalog = $json.catalog;
const products = catalog.product || [];

// Process each product
return products.map(product => ({
  json: {
    sku: product.$.sku,
    name: product.name[0],
    price: parseFloat(product.price[0]),
    category: product.category[0],
    inStock: product.$.inStock === 'true'
  }
}));

Example 5: Building XML Configuration

Scenario: Generate configuration XML from workflow data.

Workflow:

Manual Trigger → Set Config Data → JSON to XML → Write File

Set Node (config structure):

{
  "configuration": {
    "$": {
      "version": "1.0",
      "generated": "{{ $now.toISO() }}"
    },
    "database": {
      "host": "{{ $vars.DB_HOST }}",
      "port": "5432",
      "name": "{{ $vars.DB_NAME }}"
    },
    "cache": {
      "$": { "enabled": "true" },
      "ttl": "3600",
      "maxSize": "1000"
    },
    "features": {
      "feature": [
        { "$": { "name": "darkMode", "enabled": "true" } },
        { "$": { "name": "beta", "enabled": "false" } }
      ]
    }
  }
}

XML Node:

  • Mode: JSON to XML
  • Headless: Off

Output:

<?xml version="1.0" encoding="UTF-8"?>
<configuration version="1.0" generated="2024-01-15T10:30:00Z">
  <database>
    <host>db.example.com</host>
    <port>5432</port>
    <name>production</name>
  </database>
  <cache enabled="true">
    <ttl>3600</ttl>
    <maxSize>1000</maxSize>
  </cache>
  <features>
    <feature name="darkMode" enabled="true"/>
    <feature name="beta" enabled="false"/>
  </features>
</configuration>

Common Errors and Fixes

Error Reference Table

ErrorCauseSolution
”No property ‘data’ does not exist”Wrong property name specifiedCheck the actual field name in your input data
”Non-whitespace before first tag”XML has content before opening tagRemove any text/BOM before <?xml or <root>
”Unexpected close tag”Malformed XML structureValidate XML with external tool first
”Invalid character in name”Special characters in attribute keysCheck Attribute Key setting matches your JSON
”Cannot read property of undefined”Accessing non-existent pathVerify JSON structure after conversion
”Input must be a string”Passing object instead of XML stringEnsure source is string, not already-parsed JSON

Malformed XML Issues

The most common failure: XML validation differs between tools.

Online validators may be lenient, but n8n’s parser is strict. Common issues:

Missing closing tags:

<!-- Wrong -->
<item>
  <name>Widget
  <price>29.99</price>
</item>

<!-- Correct -->
<item>
  <name>Widget</name>
  <price>29.99</price>
</item>

Unclosed self-closing tags:

<!-- Wrong -->
<br>
<img src="image.jpg">

<!-- Correct -->
<br/>
<img src="image.jpg"/>

Invalid characters:

<!-- Wrong -->
<description>Price < $50 & new</description>

<!-- Correct -->
<description>Price &lt; $50 &amp; new</description>

Solution: Use an XML validator before processing. Add a Code node to clean/validate XML if your source is unreliable.

Binary Data Confusion

Problem: XML is in a file (binary) but you’re trying to convert directly.

Symptoms:

  • Conversion returns empty or errors
  • Data shows as buffer/binary

Solution: Use Extract from File node first:

  1. Add Extract from File node
  2. Set Operation to “Text”
  3. Connect to XML node
  4. XML node converts the extracted text

For complete guidance on binary data workflows, see our binary data handling guide.

Attribute Access Failures

Problem: Can’t access attributes after conversion.

Check these:

  1. Verify Attribute Key setting - Default is $. If you changed it, access with that key.

  2. Check Ignore Attributes - If enabled, attributes are discarded.

  3. Array wrapping - With Explicit Array on, even single values are arrays:

// Wrong
{{ $json.product.$.id }}

// May need array index
{{ $json.product[0].$.id }}
  1. Merge Attributes - If enabled, attributes merge with children:
// Without merge
{{ $json.product.$.id }}

// With merge
{{ $json.product.id }}

For debugging expressions, try our expression validator tool.

Pro Tips and Best Practices

1. Validate XML Before Processing

Save debugging time by validating XML upfront:

// In a Code node before XML conversion
const xml = $json.data;

// Basic validation checks
if (!xml || typeof xml !== 'string') {
  throw new Error('Input is not a string');
}

if (!xml.trim().startsWith('<')) {
  throw new Error('Content does not appear to be XML');
}

return [{ json: { data: xml } }];

2. Keep Explicit Array Enabled

The default Explicit Array setting prevents expression errors. Without it, your access pattern depends on whether there’s one or multiple elements:

// Inconsistent without Explicit Array:
$json.items.item.name  // Works for single item
$json.items.item[0].name  // Required for multiple

// Consistent with Explicit Array:
$json.items.item[0].name  // Always works

3. Use Code Node for Complex Transformations

When XML structures don’t map cleanly to your needs:

const items = $input.all();
const xmlData = items[0].json;

// Flatten nested structure
function flattenXml(obj, prefix = '') {
  let result = {};
  for (const key of Object.keys(obj)) {
    if (key === '$' || key === '_') continue;
    const value = obj[key];
    const newKey = prefix ? `${prefix}_${key}` : key;

    if (Array.isArray(value)) {
      value.forEach((v, i) => {
        if (typeof v === 'object') {
          Object.assign(result, flattenXml(v, `${newKey}_${i}`));
        } else {
          result[`${newKey}_${i}`] = v;
        }
      });
    } else if (typeof value === 'object') {
      Object.assign(result, flattenXml(value, newKey));
    } else {
      result[newKey] = value;
    }
  }
  return result;
}

return [{ json: flattenXml(xmlData) }];

4. Handle SOAP Namespaces Gracefully

Strip namespace prefixes for cleaner data:

// Remove namespace prefixes from keys
function stripNamespaces(obj) {
  if (typeof obj !== 'object' || obj === null) return obj;

  if (Array.isArray(obj)) {
    return obj.map(stripNamespaces);
  }

  const result = {};
  for (const key of Object.keys(obj)) {
    const newKey = key.includes(':') ? key.split(':')[1] : key;
    result[newKey] = stripNamespaces(obj[key]);
  }
  return result;
}

5. Test with Real Data Early

XML from documentation examples often differs from production data. Test with actual API responses or files as soon as possible to catch structural differences.

6. Use Merge Attributes Cautiously

While Merge Attributes simplifies access, it can cause issues:

  • Attribute names might conflict with element names
  • Code relying on $ structure breaks
  • Harder to distinguish attributes from children

Keep it off unless you’re certain there are no naming conflicts.

7. Handle Empty Elements

XML empty elements can become unexpected values:

<product>
  <description></description>
  <notes/>
</product>

May become:

{
  "product": {
    "description": [""],
    "notes": [""]
  }
}

Add defensive checks in your expressions:

{{ $json.product.description[0] || 'No description' }}

8. Combine with Rate Limiting for APIs

When fetching XML from external APIs, respect rate limits. See our API rate limiting guide for strategies.

When to Get Help

Some XML integration scenarios require specialized expertise:

  • Complex SOAP services with multiple WSDLs and authentication
  • High-volume XML processing requiring optimization
  • XML schema validation before processing
  • Custom XML transformations with XSLT-like requirements
  • Legacy system integration with undocumented XML formats

Our workflow development services can build production-ready XML integrations. For architectural guidance on complex data pipelines, explore our n8n consulting services.

For workflow debugging and testing, use our workflow debugger tool.

Frequently Asked Questions

Why does my XML to JSON conversion fail when online validators say the XML is valid?

Online validators often accept “mostly valid” XML that strict parsers reject. The XML node uses a strict parser that enforces proper structure. Common differences:

  1. Character encoding issues - The XML may contain invisible BOM (Byte Order Mark) characters or encoding mismatches. Try trimming the input or removing the first few characters.

  2. Missing closing tags - Visual validators sometimes auto-complete tags. Check every opening tag has a matching closer.

  3. Unescaped special characters - Characters like <, >, & inside text must be escaped (&lt;, &gt;, &amp;). Validators may display them correctly even when malformed.

  4. Invalid attribute values - Quotes in attribute values must be escaped or use alternate quote styles.

Debugging approach: Copy the XML from your n8n output (not from the original source), paste into a strict validator, and look for the specific error. The W3C XML Validator is stricter than most online tools.

How do I handle XML with namespaces like xmlns or SOAP envelopes?

Namespaces become part of property names in the JSON output. A SOAP response like <soap:Body> converts to a property named soap:Body.

Access namespaced properties:

// Bracket notation required for colons
{{ $json['soap:Envelope']['soap:Body'][0] }}

// Or in Code node
const body = $json['soap:Envelope']['soap:Body'][0];

Strip namespaces for cleaner access:

// In a Code node after XML conversion
function stripNS(obj) {
  if (!obj || typeof obj !== 'object') return obj;
  if (Array.isArray(obj)) return obj.map(stripNS);

  return Object.fromEntries(
    Object.entries(obj).map(([k, v]) => [
      k.includes(':') ? k.split(':').pop() : k,
      stripNS(v)
    ])
  );
}

return [{ json: stripNS($json) }];

This converts soap:Body to just Body, making expressions simpler.

What’s the difference between Attribute Key and Character Key settings?

These settings control how the node represents XML attributes and text content in JSON:

Attribute Key (default: $) - Where attributes appear:

<product id="123" active="true">Widget</product>

Becomes:

{ "product": { "$": { "id": "123", "active": "true" }, "_": "Widget" } }

The $ key contains all attributes as an object.

Character Key (default: _) - Where text content appears when an element has both attributes and text:

{ "product": { "$": { ... }, "_": "Widget" } }

The _ key holds the actual text content.

When elements have only text (no attributes):

<name>Widget</name>

Becomes simply:

{ "name": ["Widget"] }

No $ or _ keys needed because there are no attributes.

Changing these keys: Only change them if your JSON data already uses $ or _ for other purposes and you need to avoid conflicts.

How do I work with SOAP APIs in n8n?

SOAP APIs use XML for both requests and responses. Here’s the complete pattern:

1. Create the SOAP request:

// In a Code or Set node
const soapEnvelope = `<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetUser xmlns="http://example.com/users">
      <userId>${$json.userId}</userId>
    </GetUser>
  </soap:Body>
</soap:Envelope>`;

return [{ json: { soapRequest: soapEnvelope } }];

2. Send via HTTP Request:

  • Method: POST
  • URL: Your SOAP endpoint
  • Headers:
    • Content-Type: text/xml; charset=utf-8
    • SOAPAction: "http://example.com/users/GetUser" (check WSDL)
  • Body: {{ $json.soapRequest }}

3. Convert response with XML node:

  • Mode: XML to JSON
  • Property Name: data
  • Merge Attributes: On (optional, simplifies namespace access)

4. Extract the data you need:

// Navigate through SOAP structure
const response = $json['soap:Envelope']['soap:Body'][0];
const userData = response['GetUserResponse'][0]['User'][0];

For complex SOAP integrations with authentication and multiple operations, consider our workflow development services.

Can I convert JSON with attributes back to properly formatted XML?

Yes, but your JSON must follow the attribute/character key conventions. Here’s how to structure JSON for XML output with attributes:

Element with attributes only:

{
  "product": {
    "$": { "id": "123", "status": "active" },
    "name": "Widget",
    "price": "29.99"
  }
}

Output:

<product id="123" status="active">
  <name>Widget</name>
  <price>29.99</price>
</product>

Element with attributes AND text content:

{
  "price": {
    "$": { "currency": "USD" },
    "_": "29.99"
  }
}

Output:

<price currency="USD">29.99</price>

Common mistake: Trying to add attributes without the $ key structure:

// Wrong - this creates child elements, not attributes
{ "product": { "id": "123", "name": "Widget" } }

// Correct - uses $ for attributes
{ "product": { "$": { "id": "123" }, "name": "Widget" } }

Building the structure dynamically:

// In Edit Fields or Code node
return [{
  json: {
    invoice: {
      '$': {
        number: $json.invoiceNumber,
        date: $now.toISODate()
      },
      customer: $json.customerName,
      items: {
        item: $json.items.map(i => ({
          '$': { sku: i.sku },
          '_': i.name
        }))
      }
    }
  }
}];

This produces properly attributed XML that legacy systems expect.

How do I process XML received via webhook?

When external systems POST XML to your workflow:

Webhook configuration:

  • Set HTTP Method to POST
  • The XML arrives in the request body

Processing flow:

Webhook → XML to JSON → Process Data → Response

Access the XML:

// XML is typically in $json.body or $json.data
// Check your webhook output to find the exact field
{{ $json.body }}

Important: If the webhook receives the XML as a string, the XML node converts it directly. If it arrives as binary data, add an Extract from File node first. See our binary data handling guide for details on working with different data formats.

Ready to Automate Your Business?

Tell us what you need automated. We'll build it, test it, and deploy it fast.

48-72 Hour Turnaround
Production Ready
Free Consultation

Create Your Free Account

Sign up once, use all tools free forever. We require accounts to prevent abuse and keep our tools running for everyone.

or

By signing up, you agree to our Terms of Service and Privacy Policy. No spam, unsubscribe anytime.