n8n XML to JSON: Fix Arrays, Attributes and Missing Fields

Convert XML in n8n without breaking on one-record feeds. Learn the array and attribute settings, validate missing prices and download an eight-case workflow lab.

Converting XML in n8n is easy until the feed changes. Two products work. One product breaks your next node. A SKU loses its zeros. A missing price quietly turns into zero.

This guide builds a small n8n XML to JSON workflow that makes those changes easier to catch. You get a free lab, real screenshots and eight sample cases you can run yourself.

Quick answer

Use n8n’s XML node with XML to JSON mode and a text input field. Choose your array and attribute settings before mapping values. For the catalog example below, keep attributes separate, turn the product field into a list, then Split Out that list. Check missing values before sending records to another app.

Tested: 9 October 2026 in n8n Cloud 2.43.2. Eight synthetic XML cases passed our checks. This is a small data-shape lab, not a live supplier integration or a large-file benchmark.

Download the free XML to JSON lab

The ZIP includes the importable workflow, sample XML cases, the normalization code and setup notes. It needs no credentials and makes no external requests or writes.

Why one XML record can break a working workflow

Our first sample contains one product:

<catalog>
  <product sku="0012">
    <name>Desk lamp</name>
    <price>12.50</price>
  </product>
</catalog>

With the lab’s settings, the parsed product is an object. Add a second product and the product field becomes an array. The XML is valid in both cases. Your downstream mapping still needs to handle both.

// One product: catalog.product is an object
{"catalog":{"product":{"$":{"sku":"0012"},
  "name":"Desk lamp","price":"12.50"}}}

// Multiple products: catalog.product is an array
{"catalog":{"product":[
  {"$":{"sku":"0012"},"name":"Desk lamp","price":"12.50"},
  {"$":{"sku":"0013"},"name":"Mug","price":"0"}
]}}

These are the structures used by our one-product and two-product fixtures. The dollar-sign field contains attributes because we explicitly turned Merge Attributes off.

A practical rule: decide which fields must always be lists, which must be text, and which may be missing. Do that before connecting the final write node.

Build the n8n XML to JSON workflow

1. Put the XML in a text field

For a quick test, use an Edit Fields node in JSON Output mode with this object:

{
  "data": "<catalog><product sku=\"0012\"><name>Desk lamp</name><price>12.50</price></product></catalog>"
}

The downloadable lab uses a Code node called XML fixtures to supply eight examples instead. That lets the same conversion run against different shapes.

For an API feed, use HTTP Request → Options → Response → Response Format: Text. Put the response in data, then check that field in the output before adding XML. If you include response headers and status, inspect the resulting body location rather than assuming the field stayed the same. See the official HTTP Request reference.

For a downloaded binary file, use Extract from File with Extract From Text File. Point it at the actual binary property, set the destination text field to data and check the file’s encoding. A binary attachment and a JSON text field are different inputs.

2. Set the XML options explicitly

Add an XML node and use the following settings. These are our lab choices, not a claim that every feed needs them.

SettingLab valueWhat to check
ModeXML to JSONInput is XML text.
Property NamedataThis exact input field exists.
Explicit RootOnMapping starts at catalog.
Explicit ArrayOffWe normalize the product list ourselves.
Merge AttributesOffAttribute SKU stays separate from a child named sku.
Ignore AttributesOffProduct IDs remain available.
Attribute / Character Key$ / _Code expects these keys.
TrimOnOuter text spaces are removed.

The XML node documentation explains the options. Its source implementation also shows why setting Merge Attributes explicitly matters: the default merges them.

3. Normalize only the list you need

In our example, products must always be an array. A product’s name and price can stay simple text. Add a Code node in Run Once for All Items mode.

The core shape fix is short:

const raw = $input.first().json.catalog?.product;
const products = raw == null ? []
  : Array.isArray(raw) ? raw : [raw];

This snippet illustrates the list conversion for the first incoming feed item; it is not the complete node. Use Normalize catalog from the download for the runnable version. It also checks the root, validates product objects and maps every incoming feed item.

The full node builds these fields for each product:

  • sku: the sku attribute, kept as text.
  • name: trimmed child text.
  • priceText: the original price text, kept for review.
  • price: a number only after our format check passes.
  • status and errors: ready or review, with a reason.

Our price rule accepts non-negative decimals with up to two decimal places. It accepts 0. It flags a missing price or 12,50 for review. If your supplier uses comma decimals or another currency format, write a specific conversion rule instead of guessing.

Keep currency amounts as decimal strings or integer minor units when your destination needs exact accounting values. The lab’s numeric price is a mapping example, not a money calculation engine.

4. Split the products into n8n items

Add Split Out after Normalize catalog. Set Field to Split Out to products and Destination Field Name to product. In the lab, Selected Other Fields keeps reviewCount.

Now each item has its own product object. A later mapping can read:

{{ $json.product.sku }}
{{ $json.product.name }}
{{ $json.product.price }}
{{ $json.product.status }}

Our empty catalog produces no product items. Keep feed-level monitoring on a separate branch before Split Out so you can still record a zero-product run. The Split Out reference explains how to carry other fields into the new items.

Actual n8n XML to JSON lab with eight input fixtures, Parse XML, Normalize catalog and Split products
Our actual n8n run. The assertion branch checks the fixtures; the lower branch previews the product items. Open the image for a larger view.

5. Keep review rows away from writes

Before adding Google Sheets, a database or a CRM, place an IF or Filter node after Split Out. Allow a write only when product.status equals ready. Send review rows to a separate review path.

A ready row still needs your destination’s rules. Confirm its unique product key, decide how updates work and check the response after a write. Parsing XML does not prevent duplicate inserts.

Do not interpret a missing or empty feed as an instruction to delete existing products. Check the supplier’s feed contract first. If the API returns 429 while fetching data, use the separate n8n Retry-After guide; changing XML settings will not fix a rate limit.

What our eight XML test cases showed

We ran the importable lab in n8n Cloud. The assertion node reported 8 cases, 8 passed. The preview branch emitted eight product items across the fixtures. Two were marked review and six were ready. Review fixtures count as passing tests because the expected result was to catch bad data.

Sample caseProduct itemsObserved result
One product1One ready row; SKU 0012 preserved.
Two products2Two ready rows; a zero price stays valid.
Empty catalog0Empty products array; no product writes to perform.
Attribute and child both named sku1Attribute A-01 selected; child label does not replace it.
Leading zeros1SKU 000007 stays text.
CDATA and escaped text1Name Café <gift> preserved as text.
Missing price1Review; price is null.
Comma decimal price1Review; invalid_price under our rule.
Real n8n assertion output showing eight XML cases tested and eight passed on October 9, 2026
The real assertion output, with the code beside it. These checks cover sample conversion and validation; they do not measure production reliability.

Fix common n8n XML to JSON problems

“Item has no JSON property called data”

Open the XML node’s input and find the field containing the XML string. Match Property Name to that field. If the source is a binary file, extract its text first. Do not rename fields blindly when the real problem is a nested HTTP response body.

The Split Out node sees an object instead of a list

Test a feed with just one record. Our lab turns that record into a one-element array before Split Out. Check that you are splitting the new products field, not the original catalog.product field.

Every child field becomes an array

Check Explicit Array. Turning it on is a valid alternative when you want all child elements to have list shapes. It also changes the mapping for single name and price fields. Our normalizer expects it off; do not turn it on without updating and retesting the code.

An attribute or text value seems missing

Check Merge Attributes, Ignore Attributes and the configured keys. With our settings, an attribute lives under $. Text with its own attributes can live under _. The full normalizer reads that text shape, but it does not support repeated name or price elements.

The feed uses namespaces or a different root

The download expects catalog/product. A SOAP envelope, RSS feed or namespaced supplier document has a different path. Inspect that output and adapt the mapping. Do not remove prefixes or flatten everything before checking whether two fields would collide.

The XML itself is invalid

Inspect the source for broken closing tags and unescaped characters. Handle a parse failure separately from a valid record with a missing price. For failed production executions, see our n8n error workflow setup. This lab’s eight fixtures are well-formed XML; it does not claim a malformed-input test.

Who should use this example?

Use it if you are learning to turn a small XML product feed into predictable JSON before mapping records to another app. It is also a useful test pattern when a feed works with several records but fails with one.

Choose a different approach for huge files that need streaming, arbitrary document conversion, mixed-content publishing XML or strict XML schema validation. We did not load-test those cases. Start with the supplier’s schema and test realistic sizes in your own environment.

If your next task is CSV rather than XML, try the n8n CSV to JSON tool. For help reading nested values, use our tested n8n expression examples.

Frequently asked questions

Can n8n convert XML to JSON without a Code node?

Yes. The XML node performs the conversion. This lab adds Code for a specific product-list contract and validation; you can use Edit Fields and Split Out when your parsed shape already fits the next step.

Should I always turn Explicit Array on?

Choose it to match the shape you want downstream. It makes child elements arrays, including fields you may expect to be text. Our example keeps it off and normalizes only the product list.

Does converting XML to JSON make prices numbers?

In our fixtures, parsed price values were strings. We validated the text before converting it, while keeping the original priceText. IDs such as 000007 stayed strings.

Can I connect the downloaded workflow directly to my store?

Use a copy and replace the fixture source first. Adapt it to your real schema, remove the fixture assertion branch, filter ready rows and test your store’s update rules. The download contains no store connection or external write node.

Get the workflow and sample cases

Built by Sajjad Hasan for OperantSolo. The sample data is fictional. Node options were checked against n8n’s official documentation; screenshots and test counts come from the run described above.

Scroll to Top

Discover more from OperantSolo

Subscribe now to keep reading and get access to the full archive.

Continue reading