jsondiffpatch.net icon indicating copy to clipboard operation
jsondiffpatch.net copied to clipboard

Bug: Version 2.4.0 of jsondiffpatch.net incorrectly handles changes inside objects in arrays.

Open borland opened this issue 8 months ago • 5 comments

In https://github.com/wbish/jsondiffpatch.net/pull/72 from @dantedallag the array parsing behaviour was changed to support the new DiffArrayOptions.DetectMove. Unfortunately this appears to have broken the ability for jsondiffpatch.net to detect replaces. Here's a small C# sample app which reproduces the problem

using JsonDiffPatchDotNet;
using Newtonsoft.Json.Linq;

namespace SampleApp;

public static class Program
{
    public static void Main()
    {
        var jsonDiffer = new JsonDiffPatch(new Options { TextDiff = TextDiffMode.Simple });
        var differences = jsonDiffer.Diff(JToken.Parse(OldJson), JToken.Parse(NewJson));
        Console.WriteLine(differences.ToString());
    }

    public static readonly string OldJson =
        """
        {
          "Id": "variableset-Projects-1",
          "Variables": [
            {
              "Id": "var1",
              "Name": "Variable 1",
              "Description": null,
              "Type": "String",
              "Value": "Value 1"
            },
            {
              "Id": "var2",
              "Name": "Variable 2",
              "Description": null,
              "Type": "String",
              "Value": "Value 2"
            }
          ]
        }
        """;

    public static readonly string NewJson =
        """
        {
          "Id": "variableset-Projects-1",
          "Variables": [
            {
              "Id": "var1",
              "Name": "NewName", // This is the modified field
              "Description": null,
              "Type": "String",
              "Value": "Value 1"
            },
            {
              "Id": "var2",
              "Name": "Variable 2",
              "Description": null,
              "Type": "String",
              "Value": "Value 2"
            }
          ]
        }
        """;
}

When you run this with jsondiffpatch.net version 2.3.0, we see the following correct output. Jsondiffpatch.net has identified the Name of the first variable changes from "Variable 1" to "NewName":

{
  "Variables": {
    "_t": "a",
    "0": {
      "Name": [
        "Variable 1",
        "NewName"
      ]
    }
  }
}

When running with jsondiffpatch.net version 2.4.0, we see this incorrect output instead. Jsondiffpatch has failed to identify the replace and instead outputs a remove/add for the whole containing object.

{
  "Variables": {
    "_t": "a",
    "_0": [
      {
        "Id": "var1",
        "Name": "Variable 1",
        "Description": null,
        "Type": "String",
        "Value": "Value 1"
      },
      0,
      0
    ],
    "0": [
      {
        "Id": "var1",
        "Name": "NewName",
        "Description": null,
        "Type": "String",
        "Value": "Value 1"
      }
    ]
  }
}

When run through JsonPatchFormatter for increased legibility, the correct output is a single "op=replace" whereas the incorrect new output is a remove/add.

Notes:

  • Adding the new DiffArrayOptions = new ArrayOptions { DetectMove = true } to the diff options has no effect; the output is the same
  • Changing TextDiff mode to Efficient or adding ArrayDiff mode also has no effect.

borland avatar Apr 21 '25 22:04 borland

@borland Thanks for catching this. I will take priority on implementing a fix.

dantedallag avatar Apr 22 '25 00:04 dantedallag

@borland I have been looking into this and I am starting to suspect that this issue was present before my change, there just hasn't been a release in a while. I checked out the 2.3 release commit and the commit before mine and found the correct behavior as you have described in the 2.3 commit, but the incorrect behavior in the commit before mine.

I may be able to look into it, but it may be a bit more time as I am not familiar with the other changes that have happened since the last release.

@wbish If you have any thoughts here, let me know.

dantedallag avatar Apr 24 '25 17:04 dantedallag

@dantedallag thank you for the investigation!

borland avatar Apr 27 '25 20:04 borland

Hi there, did you end up finding out the root cause of this issue?

ciceroneves avatar May 22 '25 16:05 ciceroneves

Hey @ciceroneves, I have an idea but have not been able to put in the time to make a fix.

My initial thought, though I haven’t completely verified this, is that an empty ObjectHash function is defaulting array entry comparison to a hash of the entire entry itself, instead of defaulting to a positional comparison. I think this would follow what @borland is experience and would track with how the original library behaves.

dantedallag avatar May 28 '25 18:05 dantedallag

@ciceroneves @borland I have the fix linked here. Would love for you to verify/review.

dantedallag avatar Jun 23 '25 18:06 dantedallag

Hello @wbish - friendly ping for review here. Thank you! cc @borland and @ciceroneves as well for awareness.

azcloudfarmer avatar Jul 22 '25 18:07 azcloudfarmer

Published 2.5.0: https://www.nuget.org/packages/JsonDiffPatch.Net/2.5.0

Thanks to everyone for helping out. Special thanks @dantedallag for his code contribution.

wbish avatar Jul 23 '25 01:07 wbish