@compare-xml/core (JavaScript)

Parse and deeply compare XML strings with the same engine used by the website.

@compare-xml/core is a lightweight JavaScript/TypeScript library for deep comparison of XML documents—the same engine that powers Compare XML. It parses XML with fast-xml-parser and detects additions, deletions, and value changes between two XML structures.

Language: JavaScript/TypeScript — distributed via npm.

Installation

# npm
npm install @compare-xml/core

# yarn
yarn add @compare-xml/core

# pnpm
pnpm add @compare-xml/core

Quick Start

import { compareXML } from '@compare-xml/core';

const baseXML = '<user><name>Alice</name><age>30</age><hobbies><item>reading</item></hobbies></user>';
const contrastXML = '<user><name>Bob</name><age>30</age><hobbies><item>reading</item><item>coding</item></hobbies><email>[email protected]</email></user>';

const differences = compareXML({ baseXML, contrastXML });

console.log(differences);
// [
//   { pathSegments: ['user', 'name'], pathString: 'user.name', pathBelongsTo: 'both', diffType: 'valueChanged' },
//   { pathSegments: ['user', 'hobbies', 'item'], pathString: 'user.hobbies.item', pathBelongsTo: 'both', diffType: 'valueChanged' },
//   { pathSegments: ['user', 'email'], pathString: 'user.email', pathBelongsTo: 'contrast', diffType: 'added' },
// ]

The default byIndex method compares the single Base item with the repeated Contrast item collection as values at that path. Use repeated elements on both sides when you want index-by-index collection alignment.

Compare Options

import { compareXML } from '@compare-xml/core';

// Case-insensitive element and attribute names
compareXML({
  baseXML: '<root><Name>Alice</Name></root>',
  contrastXML: '<root><name>Alice</name></root>',
  options: { keyCaseInsensitive: true },
});
// [] (no differences)

// Case-insensitive text and attribute values
compareXML({
  baseXML: '<root><status>OK</status></root>',
  contrastXML: '<root><status>ok</status></root>',
  options: { valueCaseInsensitive: true },
});
// [] (no differences)

The parser automatically converts numeric-looking element text and attribute values to numbers. There is no separate numeric-string option in XMLCompareOptions.

Repeated-Element Comparison Methods

Repeated child elements, such as multiple <item> nodes, are compared as arrays:

import { compareXML } from '@compare-xml/core';

// 'byIndex' (default) — compares elements at the same index
compareXML({
  baseXML: '<root><items><item>1</item><item>2</item><item>3</item></items></root>',
  contrastXML: '<root><items><item>2</item><item>3</item><item>4</item></items></root>',
});

// 'lcs' — uses Longest Common Subsequence for ordered alignment
compareXML({
  baseXML: '<root><items><item>1</item><item>2</item><item>3</item></items></root>',
  contrastXML: '<root><items><item>2</item><item>3</item><item>4</item></items></root>',
  options: { arrayCompareMethod: 'lcs' },
});

// 'unordered' — treats repeated elements as a multiset
compareXML({
  baseXML: '<root><items><item>1</item><item>2</item><item>3</item></items></root>',
  contrastXML: '<root><items><item>3</item><item>2</item><item>1</item></items></root>',
  options: { arrayCompareMethod: 'unordered' },
});
// [] (no differences)

Unordered matching ignores position but not duplicate occurrence counts. See XML Comparison Rules for examples and guidance on choosing a method.

Formatting Paths

import { pathSegmentsToString } from '@compare-xml/core';

pathSegmentsToString(['users', '[0]', 'name']);
// 'users[0].name'

API Reference

compareXML

function compareXML(params: {
  baseXML: string;
  contrastXML: string;
  options?: XMLCompareOptions;
}): XMLValueDifference[];

Parses two XML strings and deeply compares their structures. It returns an empty array when the documents are equal and throws XMLValidationError when either input is not well-formed XML.

ParameterTypeDescription
baseXMLstringThe base XML string, considered the original side.
contrastXMLstringThe XML string compared against the base.
optionsXMLCompareOptionsOptional comparison behavior.

parseXML

function parseXML(xml: string): unknown;

Parses an XML string into a JavaScript value using fast-xml-parser. Attribute names are prefixed with @.

validateXML

function validateXML(xml: string): void;

Validates that a string is well-formed XML. It throws XMLValidationError for empty, whitespace-only, or malformed input. The error includes line and col properties when position information is available.

pathSegmentsToString

function pathSegmentsToString(pathSegments: string[]): string;

Converts path segments from XMLValueDifference.pathSegments into a human-readable string. Array index segments are appended without a leading dot; other segments are joined with ..

pathSegmentsToString([]);                     // ''
pathSegmentsToString(['user', 'name']);       // 'user.name'
pathSegmentsToString(['items', '[2]', 'id']); // 'items[2].id'

XMLCompareOptions

OptionTypeDefaultDescription
arrayCompareMethodXMLArrayCompareMethod'byIndex'Strategy used to compare repeated elements.
keyCaseInsensitivebooleanfalseCompare element and attribute names case-insensitively.
valueCaseInsensitivebooleanfalseCompare text and attribute values case-insensitively.

XMLArrayCompareMethod

type XMLArrayCompareMethod = 'byIndex' | 'lcs' | 'unordered';
ValueDescription
'byIndex'Compare elements pairwise at the same index; extra trailing elements are added or deleted.
'lcs'Use Longest Common Subsequence to align ordered collections.
'unordered'Match equal elements regardless of position as a multiset, including duplicate counts.

XMLValueDiffType

type XMLValueDiffType = 'added' | 'deleted' | 'valueChanged';
ValueDescription
'added'An element or attribute exists in contrastXML but not in baseXML.
'deleted'An element or attribute exists in baseXML but not in contrastXML.
'valueChanged'The value changed between Base and Contrast.

XMLValueDifference

FieldTypeDescription
pathSegmentsstring[]Path segments; array indices appear as '[n]'.
pathStringstringDot-notation path with array indices kept as bracket suffixes.
pathBelongsTo'base' | 'contrast' | 'both'Side that owns the path.
diffTypeXMLValueDiffTypeKind of difference detected at this path.

XMLValidationError

class XMLValidationError extends Error {
  line?: number;
  col?: number;
}

Thrown by validateXML and compareXML when XML validation fails. line and col are available when the parser supplies position information.

License

MIT