@compare-xml/core (JavaScript)

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

Language: JavaScript/TypeScript — distributed via npm. Implementations for other languages may follow.

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>bob@test.com</email></user>';

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

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

Compare Options

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

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

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

See Comparison Options for details on what each option does.

Array Comparison Methods

Repeated child elements (e.g. 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 minimal diff
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 arrays as multisets, ignoring element order
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)

See Array Comparison Methods for a deeper explanation of each strategy.

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, returning an array of differences. Returns an empty array when the documents are equal. Throws XMLValidationError when either input is not well-formed XML.

ParameterTypeDescription
baseXMLstringThe base XML string (the side considered the original).
contrastXMLstringThe XML string compared against the base.
optionsXMLCompareOptionsOptional settings to customize comparison behavior.

parseXML

function parseXML(xml: string): unknown;

Parses an XML string into a JavaScript object using fast-xml-parser. Attributes are prefixed with @.

validateXML

function validateXML(xml: string): void;

Validates that a string is well-formed XML. Throws XMLValidationError if the XML is empty, whitespace-only, or malformed. The error includes line and col properties when available.

pathSegmentsToString

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

Converts a path segment array (as found in XMLValueDifference.pathSegments) into a human-readable dot-notation string. Array index segments (e.g. '[0]') are appended without a leading dot; element names are joined with ..

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

XMLCompareOptions

OptionTypeDefaultDescription
arrayCompareMethodXMLArrayCompareMethod'byIndex'Strategy used to compare arrays.
keyCaseInsensitivebooleanfalseWhen true, element/attribute keys are compared case-insensitively.
valueCaseInsensitivebooleanfalseWhen true, text values are compared case-insensitively.

XMLArrayCompareMethod

type XMLArrayCompareMethod = 'byIndex' | 'lcs' | 'unordered';
ValueDescription
'byIndex'Compares array elements pairwise at the same index. Extra trailing elements are reported as added/deleted.
'lcs'Uses the Longest Common Subsequence algorithm for minimal-diff detection in ordered arrays.
'unordered'Treats arrays as multisets, matching equal elements regardless of position.

XMLValueDiffType

type XMLValueDiffType = 'added' | 'deleted' | 'valueChanged';
ValueDescription
'added'Element/attribute exists in contrastXML but not in baseXML.
'deleted'Element/attribute exists in baseXML but not in contrastXML.
'valueChanged'The value changed between base and contrast.

XMLValueDifference

FieldTypeDescription
pathSegmentsstring[]Path to the differing value as segments. Element names appear as-is; array indices appear as '[n]' (e.g. ['users', '[0]', 'name']).
pathStringstringSame path joined into dot-notation, with array indices kept as bracket suffixes (e.g. 'users[0].name').
pathBelongsTo'base' | 'contrast' | 'both'Side that owns the path. 'base' for deleted, 'contrast' for added, 'both' for valueChanged.
diffTypeXMLValueDiffTypeKind of difference detected at this path.

XMLValidationError

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

Thrown by validateXML and compareXML when XML parsing fails. The line and col properties are available when the underlying parser provides position information.

License

MIT