zjsonpatch icon indicating copy to clipboard operation
zjsonpatch copied to clipboard

Smart equals for arrays to avoid ADD/REMOVE if move with modification

Open HerrDerb opened this issue 5 months ago • 0 comments

When diffing arrays of objects, if an object was moved and also modified, the diff would generate a remove and add operation because the objects were not strictly equal. This made patches less readable/ less obvious.

Enhancement: Added a optional "smart equals" for array objects during diffing. Now, if an object is moved and modified, the diff generates a move and a replace operation, making the patch more accurate and easier to understand. Same idea as implemented in the typescript library -> https://github.com/benjamine/jsondiffpatch

Benefit: More intuitive and minimal patches for arrays of objects Better reflects intent when objects are moved and changed

Example: Usage:

  JsonNodeEqualsRefFunction objectIdEqualsRefFunction = new JsonNodeEqualsRefFunction() {
        @Override
        public Object getEqualsRef(JsonNode jsonNode) {
            return jsonNode.has("id") ? jsonNode.get("id").asInt() : jsonNode;
        }
    };
    JsonNode actualPatch = JsonDiff.asJson(first, second, DiffFlags.defaults(), objectIdEqualsRefFunction);

JSON Diff:

// Example: Move array element and modify its property

{
  "message": "Move array element and modify its property",
  "first": [
    {"id": 1, "val": "a"},
    {"id": 2, "val": "b"},
    {"id": 3, "val": "c"}
  ],
  "second": [
    {"id": 3, "val": "changed"},
    {"id": 1, "val": "a"},
    {"id": 2, "val": "b"}
  ],
  "patch": [
    {"op": "move", "from": "/2", "path": "/0"},
    {"op": "replace", "path": "/0/val", "value": "changed"}
  ]
}

With the current main or without a custom equals ref function in this branch this results in:

[
  {"op": "remove", "path": "/2"},
  {"op": "add", "path": "/0", "value": {"id": 3, "val": "changed"}}
]

HerrDerb avatar Jul 15 '25 09:07 HerrDerb