BoxLang 🚀 A New JVM Dynamic Language Learn More...

JSON-Diff

v2.3.0 Modules

JSON Diff

An ColdFusion utility for checking if 2 JSON objects have differences

Important: JSON Objects NOT Serialized JSON Strings (Please de-serialize them first)

License

Apache License, Version 2.0.

System Requirements

  • Lucee 5+
  • BoxLang 1+
  • Adobe ColdFusion 2016 (Deprecated)
  • Adobe ColdFusion 2018+

Installation

Use CommandBox CLI to install:

box install jsondiff

Setup

property name="JSONDiff" inject="JSONDiff"; //wirebox
/* or (dot) folder path to the cfc */
JSONDiff = new path.to.the.cfc.JSONDiff(); //Instantiate Object

Methods

Method Purpose
diff(first, second [, ignoreKeys, caseSensitive]) List every change between two values
diffByKey(first, second, uniqueKeys [, ignoreKeys, caseSensitive]) Compare two arrays of structs by a unique key; returns add / remove / update
isSame(first, second [, caseSensitive]) true / false equality check
summary(diffs) Count add, remove, change, and update entries
patch(original, diff) Apply a diff to the original and return the new value
diffPatch(original, diff) Return the original with each value as {old, new, type}
displayDiff(original, diff) Return an HTML string visualising the changes
visualizeDiff(first, second [, ignoreKeys, caseSensitive]) Diff two values and return the HTML view

Change type values are ADD, REMOVE, CHANGE (and SAME in diffPatch output).

Usage

diff

Returns an array of changes. path is the location of the change (struct keys and array positions, 1-based).

var original = { name: "Ann", tags: ["a", "b"], address: { city: "Rome" } };
var updated  = { name: "Ann", tags: ["a", "c"], address: { city: "Paris", zip: "75001" } };

JSONDiff.diff(original, updated);

// Result
[
    { "type": "CHANGE", "path": ["tags", 2],            "old": "b",    "new": "c" },
    { "type": "CHANGE", "path": ["address", "city"],    "old": "Rome", "new": "Paris" },
    { "type": "ADD",    "path": ["address", "zip"],     "old": "",     "new": "75001" }
]

(Order of entries may vary.)

Ignoring keys

Keys in the ignore array are skipped during comparison.

JSONDiff.diff(
    { id: 1, updatedAt: "2024-01-01", status: "open" },
    { id: 1, updatedAt: "2024-02-01", status: "closed" },
    ["updatedAt"]
);

// Result: only status is reported
[ { "type": "CHANGE", "path": ["status"], "old": "open", "new": "closed" } ]

Case sensitivity

Comparisons are case-insensitive by default. Pass true as the last argument to make them case-sensitive.

JSONDiff.diff({ name: "ann" }, { name: "ANN" });              // []
JSONDiff.diff({ name: "ann" }, { name: "ANN" }, [], true);    // one CHANGE

diffByKey

Compares two arrays of structs by identifying rows with a unique key (a string, or an array for composite keys). Great for comparing database results.

JSONDiff.diffByKey(
    [
        {'id':26,'x':480,'y':0},
        {'id':28,'x':482,'y':10},
        {'id':32,'x':480,'y':12}
    ],
    [
        {'id':25,'x':10,'y':0},
        {'id':65,'x':298,'y':0},
        {'id':32,'x':415,'y':2}
    ],
    'id'
);

// Result
{
    "remove": [   // in the first array only
        { "key": 26, "data": { "id": 26, "x": 480, "y": 0 } },
        { "key": 28, "data": { "id": 28, "x": 482, "y": 10 } }
    ],
    "add": [      // in the second array only
        { "key": 25, "data": { "id": 25, "x": 10,  "y": 0 } },
        { "key": 65, "data": { "id": 65, "x": 298, "y": 0 } }
    ],
    "update": [   // in both, with differences
        {
            "key": 32,
            "orig": { "id": 32, "x": 480, "y": 12 },
            "data": { "id": 32, "x": 415, "y": 2 },
            "changes": [
                { "key": "x", "path": ["x"], "old": 480, "new": 415 },
                { "key": "y", "path": ["y"], "old": 12,  "new": 2 }
            ]
        }
    ]
}

Composite keys

Pass an array of column names. The returned key is an array of the values (a single key returns a plain value).

JSONDiff.diffByKey(rowsA, rowsB, ["oid", "member_id"]);
// e.g. "key": [48280, 1988]

isSame

JSONDiff.isSame({ a: [1, { b: true }] }, { a: [1, { b: false }] }); // false
JSONDiff.isSame({ a: 1 }, { a: 1 });                                // true
JSONDiff.isSame("Foo", "foo");                                      // true
JSONDiff.isSame("Foo", "foo", true);                                // false (case sensitive)

patch

Applies a diff to the original and returns the resulting value. The inputs are not modified.

var original = { name: "Ann", tags: ["a", "b"] };
var updated  = { name: "Bob", tags: ["a", "b", "c"] };
var diff     = JSONDiff.diff(original, updated);

JSONDiff.patch(original, diff);

// Result (equal to `updated`)
{ "name": "Bob", "tags": ["a", "b", "c"] }

diffPatch

Returns the original structure with every value replaced by { old, new, type }. Useful for building your own UI.

var original = { name: "Ann", age: 30 };
var diff     = JSONDiff.diff(original, { name: "Bob", age: 30, email: "[email protected]" });

JSONDiff.diffPatch(original, diff);

// Result
{
    "name":  { "type": "CHANGE", "old": "Ann", "new": "Bob" },
    "age":   { "type": "SAME",   "old": 30,    "new": 30 },
    "email": { "type": "ADD",    "old": "",    "new": "[email protected]" }
}

displayDiff

Returns an HTML string of nested <ul> lists: removed/old values are red with strikethrough, added/new values are green, unchanged values are grey.

var original = { name: "Ann", age: 30 };
var diff     = JSONDiff.diff(original, { name: "Bob", age: 30 });

writeOutput( JSONDiff.displayDiff(original, diff) );

summary (diffs)

Get a count of each diff type.

    var diffs = JSONDiff.diff({a:1},{a:2,b:3});
    JSONDiff.summary(diffs);

    //Result
    {
        add: 1,
        remove: 0,
        change: 1,
        update: 0
    }

visualizeDiff (origData, newData [, ignored array of keys])

Return a simple HTML diff between two objects.

    JSONDiff.visualizeDiff({a:1},{a:2});

    //Result
    <ul><li><span style="font-weight:bold">a</span>: <span style="background: #ffbbbb;text-decoration: line-through;">1</span> <span style="background: #bbffbb;">2</span> (change)</li></ul>

License

This library is distributed under the apache license, version 2.0

Copyright 2021 Scott Steinbeck; All rights reserved.

Licensed under the apache license, version 2.0 (the "license"); You may not use this library except in compliance with the license. You may obtain a copy of the license at:

http://www.apache.org/licenses/license-2.0

Unless required by applicable law or agreed to in writing, software distributed under the license is distributed on an "as is" basis, without warranties or conditions of any kind, either express or implied.

See the license for the specific language governing permissions and limitations under the license.

Changelog

All notable changes to this project are documented here. Versions follow box.json. Only v2.0.0 is tagged in git; other entries are dated from the commit that set the version.

Breaking changes are flagged with ⚠️.

[2.3.0] - 2026-10-08

Added

  • summary(diffs) returns counts of add, remove, change, and update.
  • visualizeDiff(first, second [, ignoreKeys, caseSensitive]) diffs two values and returns the HTML from displayDiff().
  • BoxLang 1+ is a supported engine.

Changed

  • Numeric comparison uses BigDecimal instead of precisionEvaluate(), so it runs on BoxLang. numericCheck() recognizes Long, Float, Short, and BigDecimal, and ignores null or non-simple values.
  • diffByKey() grouping uses ordered structs so grouped keys keep a stable order.

Fixed

  • runPatch() accepts arrays as well as structs, so patch() can apply array diffs on BoxLang.

Notes

  • No breaking changes. visualizeDiff() HTML follows displayDiff(), including JSON serialization and change-type labels.

[2.2.0] - 2025-04-08

Added

  • caseSensitive argument (default false) on diff(), diffByKey() and isSame(). New helper isSameSimpleValue(). (#3)

Changed

  • Dev dependency testbox is now 6.3.0+13.
  • Functions declare return types and use arguments. scoping (e.g. boolean numericCheck(), boolean isSame(), array diff()).

Notes

  • Default behavior is unchanged (case-insensitive). No breaking changes expected, unless you override or call internal helpers with untyped/non-boolean values.

[2.1.1] - 2025-03-18 (approx. 2.1.x range)

Fixed

  • diffPatch() when items are added to arrays.
  • HTML output (displayDiff()) now serializes object/array changes to JSON for display.
  • diff() handles null values in arrays and objects.

Notes

  • ⚠️ Possible behavior change: diff() results for inputs containing null differ from earlier versions.

[2.0.0] - 2023-02-07 (tag v2.0.0)

Added

  • patch(original, diff) – applies a diff to a copy of the original.
  • diffpatch(original, diff) – annotates every value as {old, new, type} (SAME for unchanged).
  • displayDiff(original, diff) – returns an HTML view of the changes.
  • Helpers runPatch() and diffToHTML().
  • New specs: ArraySpec, BasicSpec, diffPatchSpec, displayDiffSpec.

Changed

  • Major version bump; diff() / diffByKey() signatures unchanged. Code formatting cleanup.

Notes

  • Readme was not updated with the new functions.

[1.1.3] - 2022-07-18

Changed

  • ⚠️ diffByKey() returns a plain value for single-column keys and an array for composite keys (e.g. [48280, 1988]), instead of a joined string ("48280_1988"). Code comparing against the string form must be updated.
  • Internal variables are cleaned up after use.

[1.1.2] - 2022-07-08

Added

  • key field on struct changes in diffByKey() results (PR #2).

[1.1.1] - 2022-06-09

Added

  • diffByKey() for arrays and queries.

Fixed

  • Error when there are no changed items.

[1.0.x] - 2021-12 to 2022-01

Added

  • Initial release: diff() with ignored-keys support, Readme, MIT license (2022-05).

Fixed

  • CF compatibility (trailing comma, prepend instead of unshift).
  • Handling of differing number types and null; numeric check refactored (isNumeric was problematic).

Upgrade guide

  • 1.x → 2.0: update any code relying on string composite keys from diffByKey() (see 1.1.3).
  • 2.0 → 2.2: no required changes; opt in to caseSensitive where needed.
  • 2.2 → 2.3: no required changes.

$ box install jsondiff

No collaborators yet.
   
5.00 / 1
  • {{ getFullDate("2021-12-16T17:07:17-06:00") }}
  • {{ getFullDate("2026-10-08T12:40:31-05:00") }}
  • 6,512
  • 19,471