Bug: Version 2.4.0 of jsondiffpatch.net incorrectly handles changes inside objects in arrays.
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 Thanks for catching this. I will take priority on implementing a fix.
@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 thank you for the investigation!
Hi there, did you end up finding out the root cause of this issue?
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.
@ciceroneves @borland I have the fix linked here. Would love for you to verify/review.
Hello @wbish - friendly ping for review here. Thank you! cc @borland and @ciceroneves as well for awareness.
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.