Quick answer: Test XML-to-JSON conversion at six boundaries: malformed markup, collection shape, attribute retention, namespace identity, mixed-content order, and scalar fidelity. Use small assertions with explicit parser settings. A successful parse only produces a representation; these tests check whether that representation still matches the data contract your application actually needs.
An XML response converts successfully, yet the client breaks when a list contains one item. Or a customer code loses its leading zeros. Six targeted XML-to-JSON conversion tests catch these contract changes before clean-looking output reaches application code.
For requests traveling the other way, use the separate JSON-to-XML acceptance checklist: outbound root names, renamed-key collisions, nil rules, and collection structure need their own receiver-facing tests.
Use the XML-to-JSON converter to inspect sanitized fixtures with attribute retention and number detection enabled or disabled. Its compact object mapping is useful for data inspection, not a promise of lossless document conversion. For background, Wikipedia's XML overview explains the format; our JSON versus XML comparison covers the broader tradeoffs.
The JavaScript assertions below were executed with this repository's fast-xml-parser dependency, version 5.11.2. Run them from a project with that version installed. Each example is independent. They test explicit settings and small fixtures, not every XML feature, external integration, or security property.
1. Does malformed XML fail before conversion starts?
Reject malformed XML before accepting any converted object. A parser producing a value is not the same as validating the input syntax, and syntax acceptance is not schema validation. Test an obviously broken fixture, require a clear failure, and separately check the fields and constraints that your application expects.
Start with the smallest negative test: a mismatched closing tag. It tells you whether the conversion boundary actually performs a syntax check, rather than merely attempting to extract a useful object. The W3C XML specification distinguishes well-formedness from validity. Those are different requirements, so name your checks accordingly.
import assert from 'node:assert/strict';
import { XMLParser, XMLValidator } from 'fast-xml-parser';
const convert = xml => {
const result = XMLValidator.validate(xml);
if (result !== true) throw new Error(result.err.msg);
return new XMLParser({ parseTagValue: false }).parse(xml);
};
assert.throws(() => convert('<orders><order></orders>'));
assert.deepEqual(convert('<orders><order>A</order></orders>'),
{ orders: { order: 'A' } });
The repository's converter follows this validate-then-parse sequence. That does not make it an XSD validator. A document can have matching tags and still omit an order identifier, use an unsupported status, or violate a partner's schema. Put those checks after syntax acceptance and before business processing.
Keep the malformed fixture in automated tests alongside a valid baseline. Assert rejection rather than a particular English error sentence, unless your own API promises that exact message. Parser upgrades can improve diagnostics without changing the behavior your application needs.
For production ingestion, define payload-size and resource limits independently. A short syntax fixture does not demonstrate resistance to hostile or exceptionally large documents. Likewise, a browser tool's successful conversion cannot establish server-side network, timeout, or access-control behavior. Keep this first test focused on one boundary: broken markup must not become accepted application data.
2. Does the list keep its shape with zero, one, and many items?
Test every collection with absent, single-item, and multiple-item fixtures. A compact XML mapping may produce a scalar or object for one occurrence and an array for repeated occurrences. Establish one destination shape for collection paths, then enforce it explicitly instead of letting a sample response define the client contract.
An integration often starts with a response containing several orders. The developer writes an array loop and moves on. The first quiet day produces one order, the parser returns a string or object, and the assumption fails far from the conversion code. Our JSON array versus object guide explains why these shapes are not interchangeable.
import assert from 'node:assert/strict';
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({ parseTagValue: false });
const orders = xml => {
const value = parser.parse(xml).orders?.order;
return value === undefined ? [] : Array.isArray(value) ? value : [value];
};
assert.deepEqual(orders('<orders/>'), []);
assert.deepEqual(orders('<orders><order>A</order></orders>'), ['A']);
assert.deepEqual(orders('<orders><order>A</order><order>B</order></orders>'),
['A', 'B']);
This normalization belongs to a known collection path. Do not apply it indiscriminately to every property: a customer's postal address is not a collection simply because its representation is inconvenient. Alternatively, configure the parser's array-selection behavior for documented paths and test that configuration directly. Consult the maintainer's parser documentation for the installed version's options.
Notice the explicit undefined check. A truthiness shortcut would treat legitimate empty strings, zero, or false as absent. Decide separately whether an empty element represents an empty item, an empty collection, or invalid input. That distinction comes from the API contract, not from how many siblings happen to appear.
After normalization, assert the destination collection's item type as well as its outer array shape. An array containing unexpected strings can still fail a consumer expecting objects. Store these three boundary fixtures together so a later parser upgrade cannot quietly reintroduce sample-dependent output.
3. Do attributes remain distinct from child elements?
Keep attributes and child elements distinguishable whenever both carry application data. Test an element whose attribute and child share the same name, then verify both destination values. A prefix such as @_ is a mapping convention, not XML syntax or a universal JSON standard, so document it for every downstream consumer.
Consider a partner response where an item's attribute identifies the transport record while its nested id identifies a product. Flattening both into id would destroy one value. The compact mapping used by this site's tool retains attributes under prefixed keys when its attribute control is enabled.
import assert from 'node:assert/strict';
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({
ignoreAttributes: false, attributeNamePrefix: '@_',
parseTagValue: false, parseAttributeValue: false
});
const data = parser.parse('<item id="record-01"><id>product-09</id></item>');
assert.equal(data.item['@_id'], 'record-01');
assert.equal(data.item.id, 'product-09');
Repeat the fixture with attributes disabled and confirm that losing them is intentional. A toggle that makes the output shorter can also remove a required identifier or a unit of measurement. Review representative fields with the consumer rather than assuming that all attributes are presentation details.
If the destination API wants ordinary names, transform deliberately into fields such as recordId and productId. Preserve a test showing which source location populates each field. This makes a mapping review concrete and prevents a future refactor from swapping equally plausible values.
Keep parser settings beside the transformation tests. The maintainer's changelog records a 2026 fix involving attribute grouping combined with order preservation; it illustrates why option combinations deserve regression fixtures. It does not mean that every attribute mapping is broken or that this example reproduces that historical issue. Test the exact options you deploy, not a loosely similar configuration from a tutorial.
4. Do namespace mappings preserve the intended field identity?
Test namespace identity before simplifying prefixed element names. Two elements can share a local name while belonging to different namespaces, and two different prefixes can identify the same namespace. Preserving prefixed keys helps inspection, but it is not a substitute for resolving namespace names when semantic identity matters to the transformation.
The W3C Namespaces specification defines names using namespace names and local names. A prefix is a binding mechanism, not a permanent business identifier. Consequently, stripping everything before a colon can make two distinct fields collide, while treating prefix spelling as identity can split equivalent fields.
import assert from 'node:assert/strict';
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({ ignoreAttributes: false, parseTagValue: false });
const data = parser.parse(
'<r xmlns:a="urn:account" xmlns:b="urn:billing">' +
'<a:id>A12</a:id><b:id>B34</b:id></r>'
);
assert.equal(data.r['a:id'], 'A12');
assert.equal(data.r['b:id'], 'B34');
assert.equal(data.r['@_xmlns:a'], 'urn:account');
This fixture verifies retention in the tested compact mapping. It does not demonstrate a namespace-aware resolver. For a production transformation, use suitable namespace-aware processing to map the two source identities to destination fields such as accountId and billingId. Include a second fixture that changes prefix spelling without changing the namespace names, and require the same application-level result.
Also include nested declarations if the upstream format permits a prefix binding to change deeper in the document. A flat lookup collected from the root is not enough for every XML document. Do not write a colon-splitting shortcut and call it namespace handling.
SAP's official XML-to-JSON transformation examples show how configured namespace mapping affects output. That is a useful reminder to document the actual converter and mapping settings. A configuration from another platform is evidence of that platform's behavior, not a portable guarantee for this JavaScript parser.
5. Does mixed content retain the text sequence you need?
Test mixed content with text before and after a nested element. An ordinary object mapping can separate child elements from text and lose the original sequence or significant spacing. If the application needs document fidelity, select an ordered representation or retain XML rather than reconstructing prose from unordered object properties.
Product descriptions, help text, and annotated messages frequently contain both text and markup. A record-oriented conversion can look convincing because all the words appear somewhere, even though their placement has changed. Do not judge fidelity by searching the output for a few expected tokens.
import assert from 'node:assert/strict';
import { XMLParser } from 'fast-xml-parser';
const xml = '<p>Before <b>bold</b> after</p>';
const compact = new XMLParser({ parseTagValue: false }).parse(xml);
assert.equal(compact.p.b, 'bold');
assert.equal(compact.p['#text'], 'Beforeafter');
const ordered = new XMLParser({
preserveOrder: true, trimValues: false, parseTagValue: false
}).parse(xml);
assert.equal(ordered[0].p[0]['#text'], 'Before ');
assert.equal(ordered[0].p[1].b[0]['#text'], 'bold');
assert.equal(ordered[0].p[2]['#text'], ' after');
Here the compact result is unsuitable for reconstructing the original sentence by simply reading one text property. The ordered result preserves the sequence checked by this fixture, but has a different shape that consumers must understand. Order preservation is not a switch that turns a document into an ordinary business object without tradeoffs.
The public converter uses a compact mapping with trimmed values; it does not expose this ordered configuration. Use the tool for inspecting data-shaped XML, and choose a dedicated document-processing path when whitespace and interleaved nodes are part of the requirement.
Extend the fixture with adjacent inline elements and meaningful spaces before approving a document workflow. If comments, processing instructions, or exact source bytes matter, specify and test those requirements separately. This example does not prove byte-for-byte round trips. Keeping the original XML alongside derived application data is often a clearer boundary than claiming that a convenient object contains every document detail.
6. Do strings and empty values survive without accidental coercion?
Test leading-zero identifiers, long integers, empty elements, and missing elements before enabling automatic type detection. Preserve identifiers as strings and convert genuine quantities under explicit range rules. Treat empty strings, absent properties, and null as separate states unless the API contract deliberately maps them together and tests that decision.
An account code that looks numeric is still a code. Converting 00123 to a number changes its representation even when arithmetic would be harmless. Large identifiers add a precision risk: MDN documents JavaScript's safe-integer range. Once rounding occurs, converting the resulting number back to a string cannot restore the original digits.
import assert from 'node:assert/strict';
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({
parseTagValue: false, parseAttributeValue: false
});
const data = parser.parse(
'<r><code>00123</code><id>9007199254740993</id><note/></r>'
).r;
assert.equal(data.code, '00123');
assert.equal(data.id, '9007199254740993');
assert.equal(data.note, '');
assert.equal(Object.hasOwn(data, 'missing'), false);
assert.equal(Number.isSafeInteger(Number(data.id)), false);
Turn off number detection in the tool when inspecting these string-sensitive values. That preserves the tested text values under this configuration; it does not validate their format. Apply length, character, or business-rule checks after preservation, before selectively converting fields that truly represent quantities.
Our JSON number precision guide explains the downstream risks. For money or exact decimal values, agree on a representation and arithmetic strategy instead of relying on automatic inference from XML text.
Finally, write the destination contract for empty values. An empty note might mean a deliberate blank, while an omitted note might mean unchanged. Neither automatically means JSON null. Validate the converted result with the contract checks described in our JSON Schema validation guide, then serialize and inspect the actual payload sent to consumers. Conversion is complete when the receiving application gets the intended shape and values, not merely when parsing stops throwing errors.