Git Product home page Git Product logo

jsondiffpatch.net's Introduction

jsondiffpatch.net

Build status NuGet

JSON object diffs and reversible patching (jsondiffpatch compatible)

Installing

Install from jsondiffpatch.net nuget website, or run the following command:

Install-Package JsonDiffPatch.Net

Usage

The library has support for the following 3 operations: Diff, Patch and Unpatch.

Diff

Diff two json objects

  var jdp = new JsonDiffPatch();
  var left = JToken.Parse(@"{ ""key"": false }");
  var right = JToken.Parse(@"{ ""key"": true }");

  JToken patch = jdp.Diff(left, right);

  Console.WriteLine(patch.ToString());

  // Output:
  // {
  //     "key": [false, true]
  // }

Patch

Patch a left object with a patch document

  var jdp = new JsonDiffPatch();
  var left = JToken.Parse(@"{ ""key"": false }");
  var right = JToken.Parse(@"{ ""key"": true }");
  JToken patch = jdp.Diff(left, right);

  var output = jdp.Patch(left, patch);

  Console.WriteLine(output.ToString());

  // Output:
  // {
  //     "key": true
  // }

Unpatch

Unpatch a right object with a patch document

  var jdp = new JsonDiffPatch();
  var left = JToken.Parse(@"{ ""key"": false }");
  var right = JToken.Parse(@"{ ""key"": true }");
  JToken patch = jdp.Diff(left, right);

  var output = jdp.Unpatch(right, patch);

  Console.WriteLine(output.ToString());

  // Output:
  // {
  //     "key": false
  // }

Advanced Usage

JsonDiffPatch.Net is designed to handle complex diffs by producing a compact diff object with enough information to patch and unpatch relevant JSON objects. The following are some of the most common cases you may hit when generating a diff:

  • Adding, Removing a property from an object
  • Changing the property value or even value type
  • Inserting and shifting elements in an array
  • Efficient string diffing using google-diff-match-patch
  • Nested object diffs

The full JSON patch document format is documented at https://github.com/benjamine/jsondiffpatch.

var left =
{
  "id": 100,
  "revision": 5,
  "items": [
    "car",
    "bus"
  ],
  "tagline": "I can't do it. This text is too long for me to handle! Please help me JsonDiffPatch!",
  "author": "wbish"
}

var right =
{
  "id": 100,
  "revision": 6,
  "items": [
    "bike",
    "bus",
    "car"
  ],
  "tagline": "I can do it. This text is not too long. Thanks JsonDiffPatch!",
  "author": {
    "first": "w",
    "last": "bish"
  }
}

var jdp = new JsonDiffPatch();
var output = jdp.Diff(left, right);

// Output:
{
  "revision": [   // Changed the value of a property
    5,            // Old value
    6             // New value
  ],
  "items": {      // Inserted and moved items in an array
    "0": [
      "bike"
    ],
    "_t": "a",
    "_1": [
      "",
      1,
      3
    ]
  },
  "tagline": [    // A long string diff using google-diff-match-patch
    "@@ -2,10 +2,8 @@\n  can\n-'t\n  do \n@@ -23,49 +23,28 @@\n  is \n+not \n too long\n- for me to handle! Please help me\n+. Thanks\n  Jso\n",
    0,
    2
  ],
  "author": [     // Changed the type of the author property from string to object
    "wbish",
    {
      "first": "w",
      "last": "bish"
    }
  ]
}

JSON Patches (RFC 6902)

A diff result can be converted into JSON patches, according to the RFC 6902 spec.

var left = JObject.Parse("{ \"name\": \"Justin\" }");
var right = JObject.Parse("{ \"name\" : \"John\", \"age\": 34 }");
var patch = new JsonDiffPatch().Diff(left, right);
var formatter = new JsonDeltaFormatter();
var operations = formatter.Format(patch);

/*
operations: [
  { "op": "replace", "path": "/name", "value": "John" },
  { "op": "add", "path": "/age", "value": 34 }
]
*/

Attributions

jsondiffpatch.net's People

Contributors

adam-mccoy avatar arbitertl avatar cornz avatar danielcrenna avatar dependabot[bot] avatar justinbhopper avatar khiemnd777 avatar lingbohome avatar mk-enet avatar norvegec avatar pmg23 avatar wbish avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsondiffpatch.net's Issues

Comparing array items by identifier

Is it possible to compare array items by specified identifier? For example by id, so left[0] to right[0], left[2] to right[1]? (Zero based indexed):

var left = [
  {
    "Id": 1,
    "Name": "red"
  },
  {
    "Id": 2,
    "Name": "green"
  },
  {
    "Id": 3,
    "Name": "blue"
  }
];

var right = [
  {
    "Id": 1,
    "Name": "red"
  },
  {
    "Id": 3,
    "Name": "black"
  }
]

Newtonsoft versions > 8.0.2 not supported

The nuspec file suggests that this library supports any Newtonsoft version >= 8.0.2. However, because the .csproj file references the version this does not currently work if you have a later version of Newtonsoft.

I would suggest fixing this by either changing the *.csproj files replacing

<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">

with

<Reference Include="Newtonsoft.Json, Culture=neutral">.

Alternatively, if you just changed the nuspec file to say that version 8.0.2 is required then this would at least make it clear that only version 8.0.2 is supported. To change this you could update the .nuspec file replacing <dependency id="Newtonsoft.Json" version="8.0.2" /> with <dependency id="Newtonsoft.Json" version="[8.0.2]" />.

I'm happy to open a pull request for this if it helps, but would like clarification on which approach to take.

When arrays are same, diff is stil non-null

Hi, when I compare two same arrays, I still get a non-null diff object.

When comparing these two objects:
{{
"@context": [
"http://www.w3.org/ns/csvw",
{
"@language": "en",
"@base": "http://example.org"
}
]
}}

{{
"@context": [
"http://www.w3.org/ns/csvw",
{
"@language": "en",
"@base": "http://example.org"
}
]
}}

diff = jdp.Diff(expected, actual) returns
{{
"@context": {
"_t": "a"
}
}}

I believe the result should be null in this case.

TextDiffMode.Efficient produces unexpected results

In ran into some weird results when comparing string values longer than 50 characters. A quick look at the code reveals that under the default settings TextDiffMode.Efficient is used for these particular strings. Switching to TextDiffMode.Simple solves the problem, but it seems like there is an issue in Efficient mode.

I diffed the following objects:
{ "Title": "SPRINT 2: Koppeling Afas naar Nop 2 + functionaliteiten" }
and
{ "Title": "SPRINT 2: Koppeling Afas naar Nop 2 + functionaliteiten - SYNCED" }
and got this result:
{ "Title": [ "@@ -48,8 +48,17 @@\n liteiten\n+ - SYNCED\n", 0, 2 ] }
where I expected:
{ "Title": [ "SPRINT 2: Koppeling Afas naar Nop 2 + functionaliteiten", "SPRINT 2: Koppeling Afas naar Nop 2 + functionaliteiten - SYNCED" ] }

Is this by design? If so, how can I get the actual diff? I want the actual diff to create patch requests for objects stored in a NoSql database.
Will ArrayDiffMode.Efficient produce similar results?

Array detectMove

Hi,
Any chance you could add a detectMove parameter for array comparison, so reordered arrays with matching elements are not detected as a difference?
i.e. these two objects would match?

{'name':'John Doe','positions':[ 1, 2, 3 ] }
{'name':'John Doe','positions':[ 1, 3, 2 ] }

Excluded Paths Format For Arrays

We are using the library in some integration tests for an API to validate that responses from the API match an expected response.

We want to ignore some of the fields in the API response but we aren't sure in what format we need to specify the excluded paths as the objects are within an array.

An example of the structure is shown below.
{ "items": [{ "firstKey": "firstValue", "secondKey": "secondValue". }] }

How would we specify "secondKey" to be excluded via ExcludedPaths? We have tried "secondKey", "items.secondKey", "items/secondKey" and "items.0.secondKey" but not seem to work.

JSON Delta Formatter remove operation does not keep the previous value

I've got the following inputs and I'm using the JSON Delta formatter:

            {
                person = new
                {
                    name = "John Doe",
                    dob = DateTime.Now
                },
                addresses = new List<string>() { "address 1", "address 2" }
            };

            dynamic newObject = new
            {
                person = new
                {
                    name = "Patrick",
                },
                addresses = new List<string>() { "address 1", "address 3" }
            };

The response is:

{"path":"/person/dob","op":"remove","from":null,"value":null}

If I were using the default formatter, I would have for the dob field something like that:

 "dob": [
      "2021-10-14T15:41:57.7683884-03:00",
      0,
      0
    ]

I would expect to be able to have the previous value in the JSON Patch response too.

I'm creating my own formatter inheriting from the JsonDeltaFormatter class and overriding this method:

protected override void Format(DeltaType type, JsonFormatContext context, JToken delta, JToken leftValue, string key, string leftKey, MoveDestination movedFrom)

More specifically this line, that only takes the context into account, without the delta:

However, if this is not working as designed I'd rather open a PR.

Patching string value does not work

Hi,

I have run into an issue when a string value is not correctly patched.

The minimal code to reproduce it is here

var diffPatch = new JsonDiffPatchDotNet.JsonDiffPatch();
var left = JToken.Parse(@"{ ""value"": ""username=administrator&key=333&project_id=1""}");
var rigth = JToken.Parse(@"{ ""value"": ""username=administrator&key=582eb3a9b422d1925c96daa2d4f86f6c&project_id=1"" }");
var patch = diffPatch.Diff(left, right); // get the patch
var output = diffPatch.Patch(left, patch); // apply the patch

and the output (left + patch, where patch is the difference between left and right) is { "value": "key=582eb3a9b422d1925c96daa2d4f86f6c&pro" } which is not the value of right.

the patch

{
  "value": [
     "@@ -24,11 +24,40 @@\n key=\n-333\n+582eb3a9b422d1925c96daa2d4f86f6c\n &pro\n",
     0,
     2
  ]
}

Am I missing something? Is there a way how to turn off this patch mode for long(ish) strings?

PS: It works in the master branch, I am experiencing issues with package 1.0.5.0 and .NET 4.6.1.

<package id="JsonDiffPatch.Net" version="1.0.5.0" targetFramework="net461" />

Thanks,
Karel

Unpatch breaks for unknown reasons

Hi,

I recently discovered that unpatch doesn't work on some larger strings (I can't pinpoint the exact issue).
In order to recreate a scenario that breaks (text patch failed) I used the following anonymized text pairs:

var jdp = new JsonDiffPatch();
var left = JToken.Parse("{ \"key\": \"aaaa aaaaaa aaaa aaaaaaa: aaaaaaaaa aaaa aaaaaaaa aaaa: 31-aaa-2017 aaaaa aaaaa aaaaaaa aaaa aaaaaaaa aaaa: 31-aaa-2017aaaaaa aaaaaa: aaaaaaaaa aaaaaa: aaaaaa aaaaa aaaaa aaaaaaa aaaaaa: aaaaaaaaaa aaaaaaaa aaaaaaa: aaaa(aaaa aaaaaa/aaaaaaaaaaaa)-aaaaaaa(aaaaaaaaaa/aaaaaaaaaa aaaa aaaaaa)aaaaa aaaaa aaaaaaa:aaaaaa aaaaaaa: aaaaaaaa aaaaaaaaaa aaaaaaa: aaaaaaaaaaaa aaaaaaaaaa: aaaaaaaa-aaaaaaaaaaaaaaaa aaaaaaaaaa aaaaaa: aaaaaaaaaaaaaaaa aaaaaaaaaa aaaaa: aaaaaaaa aaaaa-aaaaa aaaaaaaaaa, aaaaa aaaaaaa aa aaaaaaa aaaaaaaaaaaa aaaaa aaaaaaaaaaa (aaaaaa), aaaaa a 100 aaaaa aa aaa aaaaaaa.aaa aaaa: aaaaaaaaaaaaaaaa: aaaaaaaaaaaa aaaaaaaa: aaa aaaaa aaaaa:aaaaaaa aaaaaaa: 31-aaa-2014aaaaaa aaaaa: 16-aaa-2016aaaaaa aaaaa: 30-aaa-2017aaaaaa aaaaa: 27-aaa-2017aaaaaa aaaaa: 31-aaa-2017aa aaaaaaaaaa aaaaaaaaaa, (aaaaa aa aaaa aa a 52.67 aaaaa aa aaaa aaa aaaa aaaaaa aaaaaa), aaaaa 100 aa aaaa aaaaaaa.aaaaaaa aaaaaaa: 16-aaa-2016aaaa aaaaaaa aa 100 aaaaa aa aaaa aaa aaaa aaaaaa aaa aa aaaaaaaaaa aaa aaaaaaaaaa, a 88.02 aaaaaaaaaa aa aaaa aaa aaaa aaaaaa aaaaaa.aaaaaaa aaaaaaa: 30-aaa-2017aaaa aaaaaaa aa 100 aaaaa aa aaa-aaaa aaaaaa, aaaaa aa 100 aaaaa aa aaaa aaa aaaa aaaaaa aaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa aaa, aaaaa aa aaaa aa 65.656 aaaaa aa aaaa aaa aaaa aaaaaa aaa aa aaaaaaaaaa aaa aaaaaaaaaa aaa 34.343 aa aaaa aaa aaaa aaaaaa aaaaaa. aaaa aaa aaaa aaaaaa aaa aa aaaaaaaaaa aaa aaaaaaaaaa aa 88.02 aaaaa aa aaaa aaa aaaa aaaaaa aaaaaa.aaaaaaa aaaaaaa: 27-aaa-2017aaaa aaaaaaa aa 100 aaaaa aa aaa-aaaa aaaaaa, aaaaa\" }");
var right = JToken.Parse("{ \"key\": \"aaaa aaaaaa aaaa aaaaaaa: aaaaaaaaa aaaa aaaaaaaa aaaa: 17-aaa-2017 aaaaa aaaaa aaaaaaa aaaa aaaaaaaa aaaa: 17-aaa-2017aaaaaa aaaaaa: aaaaaaaaa aaaaaa: aaaaaa aaaaa aaaaa aaaaaaa aaaaaa: aaaaaaaaaa aaaaaaaa aaaaaaa: aaaa(aaaa aaaaaa/aaaaaaaaaaaa)-aaaaaaa(aaaaaaaaaa/aaaaaaaaaa aaaa aaaaaa)aaaaa aaaaa aaaaaaa:aaaaaa aaaaaaa: aaaaaaaa aaaaaaaaaa aaaaaaa: aaaaaaaaaaaa aaaaaaaaaa: aaaaaaaa-aaaaaaaaaaaaaaaa aaaaaaaaaa aaaaaa: aaaaaaaaaaaaaaaa aaaaaaaaaa aaaaa aaaa: -2016aaaaaaaaa aaaaaaaaaa aaaaa: aaaa aaaaaaa aa 100 aaaaa aa aaa-aaaa aaaaaa aaa, aaaaaaaa aaaaaaaaaa aa aaaaaa.aaaaaaaaa aaaaaaaaaa: aaaaaaaa-aaaaaaaaaaaaaaaa aaaaaaaaaa aaaaaa: aaaaaaaaaaaaa aaaaaaaaaa aa aaaa: -2016aaaaaaaaa aaaaaaaaaa aaaaa: aaaaaaaa aaaaa-aaaaa aaaaaaaaaa, aaaaa aaaaaaa aa aaaaaaa aaaaaaaaaaaa aaaaa aaaaaaaaaaa (aaaaaa), aaaaa a 100 aaaaa aa aaa aaaaaaa.aaa aaaa: aaaaaaaaaaaaaaaa: aaaaaaaaaaaa aaaaaaaa: aaa aaaaa aaaaa:aaaaaaa aaaaaaa: 31-aaa-2014aaaaaa aaaaa: 16-aaa-2016aaaaaa aaaaa: 30-aaa-2017aaaaaa aaaaa: 27-aaa-2017aaaaaa aaaaa: 31-aaa-2017aaaaaa aaaaa: 16-aaa-2017aa aaaaaaaaaa aaaaaaaaaa, (aaaaa aa aaaa aa a 52.67 aaaaa aa aaaa aaa aaaa aaaaaa aaaaaa), aaaaa 100 aa aaaa aaaaaaa.aaaaaaa aaaaaaa: 16-aaa-2016aaaa\" }");

JToken patch = jdp.Diff(left, right);

var output = jdp.Unpatch(right, patch);

Thanks for porting the great lib! :)

Add support for .NET Standard

This is just what I need. Unfortunately, I need it for .NET Standard. Could you make it compatible with .NET Standard?

Unpatch throws exception

Using the attached files, I am unable to 'Unpatch' a file. While the json is fairly complex, the changes are not that complex. Is this a bug?

exception - Index is equal to or greater than Count.
Parameter name: index

string modifiedJson = File.ReadAllText($"c:/temp/modified.json");
string defaultJson = File.ReadAllText($"c:/temp/default.json");

JsonDiffPatch jsonDiffPatch = new JsonDiffPatch();
var patch = jsonDiffPatch.Diff(modifiedJson, defaultJson);

Console.WriteLine("patch" + patch);

try
{
string patched = jsonDiffPatch.Unpatch(defaultJson, patch);
Console.WriteLine("patched" + patched);
}
catch (Exception ex)
{
Console.WriteLine("exception" + ex.Message);
}
default.txt
modified.txt

What is the "From" field for?

Hi! I started looking at this project and playing around with it and it does just what I need, but the Operation.From field always returns null? Is this supposed to be the old value? Ive looked at the code and it doesnt look like its ever used.

How is Operation.From supposed to be used?

Is there an equiv to propertyFilter to filter out properties?

When conducting a diff between two objects, they will always have different timestamps (a key property in our object). Is there a way to conduct a diff ignoring that field?

In jsondiffpatch there is a propertyFilter function you can override that would allow you to ignore a property. Does that exist in jsondiffpatch.net?

High vulnerability in NewtonSoft.json 11.0.1

Hi William :)
My name is Bar and I'm using your great NuGet for finding differences between Jsons.
I encounter one issue- JsonDiffPatch.net v2.3.0 utilizes NewtonSoft.json 11.0.1 which holds a high vulnerability.
It is fixed in Newtonsoft.Json v13.0.1. Wondered if you update the NuGet with an upgrade to NewtonSoft.json?

Thank you!

Exception of type 'System.OutOfMemoryException' was thrown using - JsonDiffPatch()

Hi ,

I have two list lstA(33727 records) and lstB(34238 records). Converting those as json string and trying to compare using JsonDiffPatch. code below

lstA = GetRateCard();
lstB = rates.Meters.ToList();

var first = JsonConvert.SerializeObject(lstA);
var second = JsonConvert.SerializeObject(lstB);

var jdp = new JsonDiffPatch();
var output = jdp.Diff(first, second); ----->>> getting the out of memory exception screen shot also attached.

error

stack trace:
at JsonDiffPatchDotNet.Lcs.LcsInternal(List1 left, List1 right) in c:\Dev\wbish_github\jsondiffpatch.net\Src\JsonDiffPatchDotNet\Lcs.cs:line 31
at JsonDiffPatchDotNet.Lcs.Get(List1 left, List1 right) in c:\Dev\wbish_github\jsondiffpatch.net\Src\JsonDiffPatchDotNet\Lcs.cs:line 24
at JsonDiffPatchDotNet.JsonDiffPatch.ArrayDiff(JArray left, JArray right) in c:\Dev\wbish_github\jsondiffpatch.net\Src\JsonDiffPatchDotNet\JsonDiffPatch.cs:line 399
at JsonDiffPatchDotNet.JsonDiffPatch.Diff(JToken left, JToken right) in c:\Dev\wbish_github\jsondiffpatch.net\Src\JsonDiffPatchDotNet\JsonDiffPatch.cs:line 54
at JsonDiffPatchDotNet.JsonDiffPatch.Diff(String left, String right) in c:\Dev\wbish_github\jsondiffpatch.net\Src\JsonDiffPatchDotNet\JsonDiffPatch.cs:line 276
at DomainOperations.RateOperations.DailyRates(DataResponse rates) in E:\Cloud Service Providers Project\BILLINGAPI\DomainOperations\RateOperations.cs:line 308

However if i try with few records saw 2000 each i am able see the expected results. not sure how to deal with more data ? need help

Thanks
Dev.

Is there any way to ignore array order when diffing?

Say I create a diff for these 2 arrays

var a1 = @"
{
    "items": [
        "one",
        "two"
}";

var a2 = @"
{
    "items": [
        "two",
        "one"
}";

Is there some way of causing the diff to recognise them as the same array, since they only differ in the items' order?

I am using Simple mode for Array diffing

`~` and `/` show be replaced in patch path

As rfc6901#section-3 documented, we should replace ~ with ~0 and replace / with ~1.

This is Also the problem I encounted when trying to patch k8s resources, error message: the server rejected our request due to an error in our request.

Json Patch

A JSON Pointer is a Unicode string (see [RFC4627], Section 3)
containing a sequence of zero or more reference tokens, each prefixed
by a '/' (%x2F) character.

Because the characters '~' (%x7E) and '/' (%x2F) have special
meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/'
needs to be encoded as '~1' when these characters appear in a
reference token.

Array gaps not handled efficently

Consider the following two sorted arrays:

Left: [1,2,3,4,5,6,7,8,9]
Right: [1,3,4,6,8]

Under the reference (JS) implementation we get an efficent output:

{
  "_t": "a",
  "_1": [
    2,
    0,
    0
  ],
  "_4": [
    5,
    0,
    0
  ],
  "_6": [
    7,
    0,
    0
  ],
  "_8": [
    9,
    0,
    0
  ]
}

telling us clearly that items at indexes 1, 4, 6 & 8 (i.e. values 2, 5, 7 & 9) have been removed.

Under jsondiffpatch.net

void Main()
{
	var leftArray = new int[] {
		1,
		2,
		3,
		4,
		5,
		6,
		7,
		8,
		9,
	};

	var rightArray = new int[] {
		1,
		3,
		4,
		6,
		8
	};

	var jdp = new JsonDiffPatch();
	var output = jdp.Diff(JToken.FromObject(leftArray), JToken.FromObject(rightArray));
	
	Console.Out.WriteLine(output.ToString());
}

We get the following output:

{
  "_t": "a",
  "_1": [
    2,
    0,
    0
  ],
  "_2": [
    3,
    0,
    0
  ],
  "_3": [
    4,
    0,
    0
  ],
  "_4": [
    5,
    0,
    0
  ],
  "_5": [
    6,
    0,
    0
  ],
  "_6": [
    7,
    0,
    0
  ],
  "_7": [
    8,
    0,
    0
  ],
  "_8": [
    9,
    0,
    0
  ],
  "1": [
    3
  ],
  "2": [
    4
  ],
  "3": [
    6
  ],
  "4": [
    8
  ]
}


Method not found on versions 2.0.2 and 2.1.0

Hello,

I've recently upgraded the package. The version 2.0.1 was working like a charm.

The 2.1.0 fails this this error:

Failed: System.MissingMethodException: Method not found: 'Newtonsoft.Json.Linq.JToken JsonDiffPatchDotNet.JsonDiffPatch.Diff(Newtonsoft.Json.Linq.JToken, Newtonsoft.Json.Linq.JToken)'.
System.MissingMethodException
Method not found: 'Newtonsoft.Json.Linq.JToken JsonDiffPatchDotNet.JsonDiffPatch.Diff(Newtonsoft.Json.Linq.JToken, Newtonsoft.Json.Linq.JToken)'.
at TestsHelpers.TestsHelper.CompareTwoFiles(String expectedFileName, String resultFilePath)
...

The 2.0.2 as well. Apparently, the 2.0.1 is not available anymore.

Please help,

Regards,
Nicolas

JSON Difference doesn't work properly when using array of object

I am using JSON Patches (RFC 6902) to get the difference between Jsons in RFC 6902 format
When I am trying to get the differences between two JSONs, that contains Json array and If there happens to be a value or property of an object present in an array that differs between Jsons, the package is returning the address of the whole object instead of pointing to the particular property or value.

for eg:
json1:
{
"names": [
"name1",
"name2",
"name3"
]
}

json2:

{
"names": [
"name11",
"name2",
"name33"
]
}

I get output which points to path ->
path : "/names/2"
operation: remove
path : "/names/1"
operation: remove
path : "/names/0"
operation: remove

And so on...

Whereas I was expecting the output to not point out to 1st index path (i.e. path: "/names/1") as the value is same in both JSON.

Case sensitivity and order while comparing values

  1. Is there any way in this library to ignore cases while comparing ?
  2. Also to ignore the order of values in array

Example: array1 = [ "Audi", "toyota", "merc"]; array2 = [ "toyota", "audi", "merc"];

output: It should treat both array1 and array2 as same (as they have same values in different order and different cases i.e. capital and lowercases)

"JSON RFC 6902 does not support TextDiff" and "TextDiffMode.Efficient"

Hi,
I understand there is currently no progress in this project, but I wanted to share:

If I use "new JsonDiffPatch(new Options { TextDiff = TextDiffMode.Efficient })" (which seems to be the default), I get "JSON RFC 6902 does not support TextDiff" when comparing a property value:

old: ABS DG DOC Restrictions as per Case 00260586 implemented
new: Conning Position corrected

If I switch to "TextDiffMode.Simple", its fine and I could not see any limitations (in my list of approx. 5000 items).

rdgs

2 Deletions in an array show deletion of a block and addition of a second

I have a long list of string and deletions of two elements spaced about 10 or so apart results in a diff showing a deletion of a whole chunk followed by the addition of all the elements in between the deleted elements. Is this the intended output, if so, is there a setting that can turn this off?

Support for VS2010

Hi wbish - Great work!
I only have VS2010 and the following lines do not compile:

result[$"{index}"] = new JArray(right[index]);
result[$"_{index}"] = new JArray(left[index], 0, (int)DiffOperation.Deleted);
result[$"{index}"] = diff;

I can successfully compile them if I change them to:

result.Add(new JProperty(index.ToString(), new JArray(right[index])));
result.Add(new JProperty("_"+index.ToString(), new JArray(left[index], 0, (int)DiffOperation.Deleted)));
result.Add(new JProperty(index.ToString(), diff));

When I run your example shown here I do not return the same patch output. Are my changes valid for VS2010?

Thanks in advance.

Support for .NET 3.5

This library is a great fit for a project I'm working on, but unfortunately I am "stuck" on .NET 3.5 for the time being.

I'm wondering if you'd be open to a PR adding support for .NET 3.5? I know this potentially introduces additional support burden down the road, so I understand if that's not desirable.

It looks like the only code change needed (at this time) would be here:

internal static string UrlEncode(string str)
{
str = WebUtility.UrlEncode(str);
return Regex.Replace(str, "(%[0-9A-F]{2})", encodedChar => encodedChar.Value.ToLowerInvariant());
}
internal static string UrlDecode(string str)
{
return WebUtility.UrlDecode(str);
}

This is because System.Net.WebUtility was introduce in .NET 4.0.

The required change (after adding 3.5 as a build target) would be to replace the use of WebUtility.UrlEncode and WebUtility.UrlDecode with something equivalent. I think that System.Uri.EscapeDataString and System.Uri.UnescapeDataString would do the trick for the purposes of this library.

Or rather than replacing, one could add conditional compilation like this to make this a 3.5-only change:

	internal static string UrlEncode(string str)
	{
#if NET35
		str = Uri.EscapeDataString(str);
#else
		str = WebUtility.UrlEncode(str);
#endif
		return Regex.Replace(str, "(%[0-9A-F]{2})", encodedChar => encodedChar.Value.ToLowerInvariant());
	}

	internal static string UrlDecode(string str)
	{
#if NET35
		return Uri.UnescapeDataString(str);
#else
		return WebUtility.UrlDecode(str);
#endif
	}

Let me know what you think!

Suggestion for further compatibility with Microsoft.AspNetCore.JsonPatch

Hi,

My suggestion is not to have a separate Operation class but instead use Microsoft.AspNetCore.JsonPatch.Operations.Operation which is basically the same.

So, through this, I can convert your diff after formatting it to IList into JsonPatchDocument through constructor, which currently I cant since even though your operation class is exactly same with microsoft one, it cannot cast.

Best,

Add support for custom diff comparers

A common theme that has been developing is people that want to customize the output of the patch object:

#20 wants to create a compact json patch object where each left value is omitted -- does not care about unpatch functionality.
#21 wants to skip adding to the patch document cases where the same key exists but the value is different. This means edits are not included in the patch document -- only adds and deletes. Essentially wants to see if the structure of the document has changed -- not so much the contents of each key.
#18 seeks to find diff of a subset of the document.

I do not want to change the default behavior of JsonDiffPatch because I think the main value add of the library is to support straightforward diff/patch/unpatch.

However, I think there is a valid case to make for JsonDiffPatch support custom diff comparers that can have user defined behavior.

JsonDiffPatch(Option, IComparer)

I have not thought this through, but the interface might look something like the following:

IComparer

  • handleEdit(path, key, left, right)
  • handleAdd(path, key, right)
  • handleDelete(path, key, left)

This is not really diff patch output formatter as the output would still technically be supported by patch and unpatch -- however, those methods may just have weird behavior if someone implements a strange IComparer.

Add support for conflict resolution (mutated json objects)

Hi! Is it a bug? Or it is not possible to apply a patch to changed json?
I created a patch on 'a' object, then cleared array of 'a' and tried to apply that patch
but got following error:

[Test]
public void test()
{
    var a = new Foo { A = new[] { 1 } };
    var b = new Foo { A = new int[0] };

    var jsonA = JsonConvert.SerializeObject(a);
    var jsonB = JsonConvert.SerializeObject(b);

    var jdp = new JsonDiffPatch();
    var diff = jdp.Diff(jsonA, jsonB);

    /////

    a.A = new int[0];
    jsonA = JsonConvert.SerializeObject(a);
    Console.WriteLine(jdp.Patch(jsonA, diff));
}

private class Foo
{
    public int[] A { get; set; }
}

System.ArgumentOutOfRangeException : Index is equal to or greater than Count.
Parameter name: index
at Newtonsoft.Json.Linq.JContainer.RemoveItemAt(Int32 index)
at JsonDiffPatchDotNet.JsonDiffPatch.ArrayPatch(JArray left, JObject patch)
at JsonDiffPatchDotNet.JsonDiffPatch.Patch(JToken left, JToken patch)
at JsonDiffPatchDotNet.JsonDiffPatch.ObjectPatch(JObject obj, JObject patch)
at JsonDiffPatchDotNet.JsonDiffPatch.Patch(JToken left, JToken patch)
at JsonDiffPatchDotNet.JsonDiffPatch.Patch(String left, String patch)
at jtest.test() in jtest.cs:line 29

ArrayDiff with ExcludePaths incorrectly fails

When comparing an object containing an array, using an exclude path for a property in that array incorrectly returns { "_t": "a" }.

Example:

{
    "User": {
        "Adresses": [
            {
                "Id": "eb369327-2321-4ae2-bb2f-4d74dff1a70d",
                "Street": "Streetname"
            }
        ]
    }
}

In this example, I want to compare this object. But I want to ignore the Id property. Using "User.Adresses[0].Id" in exclude paths, I can ignore this value.

But the result of the Diff is :

{
  "User": {
    "Adresses": {
      "_t": "a"
    }
  }
}

I see the following in the source code in the beginning of the ArrayDiff method:

var result = JObject.Parse(@"{ ""_t"": ""a"" }");

Because there is no difference between the two values (ignoring the excluded paths), this result object is returned. In this case, when there is no difference, this method should return null instead.

Json values are not compared according to json standards

According to Jsondiffpatch those two objects are differents:

{ temperature = 10.0 } and { temperature = 10 }

But according to json standards, those two objects should be the same because temperatures have the same value, and json standards does not differentiate float types and integer types.

However Json.net differentiate float types and integer types, and JValues are different if their types are different. In Jvalue.cs:

    private static bool ValuesEquals(JValue v1, JValue v2)
    {
        return (v1 == v2 || (v1._valueType == v2._valueType && Compare(v1._valueType, v1._value, v2._value) == 0));
    }

And Jsondiffpatch compares values based on the JValue.ValuesEquals function.

As a result, if I write a document { temperature = 0.0 } in CosmosDB, I retrieve a document { temperature = 0 } that Jsondiffpatch considers as different.

I asked ComosDb support and they responsded:
The JSON type system as per standard does not have two separate types for integers and floating point numbers, and these values are considered equivalent by our system.
If you want to preserve the text representation as is, you can encode this property as a string i.e. with quotes, but note for example that the string ordering rules will apply if you try to do queries with filters over this property using operators such as < or >.

google-diff-match-patch cannot be applied if it has more than one patch operation on property

JsonDiffPatch.Patch throws "Invalid textline" exception in case there are more than one operation in efficient string patch:

else if (patchArray.Count == 3) // Delete, Move or TextDiff
{
    if (patchArray[2].Type != JTokenType.Integer)
        throw new InvalidDataException("Invalid patch object");

    int op = patchArray[2].Value<int>();

    if (op == 0)
    {
        return null;
    }
    else if (op == 2)
    {
        var dmp = new diff_match_patch();
        List<Patch> patches = dmp.patch_fromText(patchArray[0].ToObject<string>());

        if (patches.Count != 1)
            throw new InvalidDataException("Invalid textline"); //<======= THIS IS A VALID CASE!

        string right = dmp.diff_text2(patches[0].diffs);
        return right;
    }
    else
    {
        throw new InvalidDataException("Invalid patch object");
    }
}

JsonDeltaFormatter.ReorderOps(..) isn't fully functional

I feel like the current approach isn't covering enough. Not only removing has to obey order when it comes to arrays, but also adding and replacing. Take those jsons for example that have filler null-values:

left:

{"foo": [null, {"id": "BAZ_01", "bar": [0, 1]}, null, null, null]}

right:

{"foo": [null, null, null, {"id": "BAZ_02", "bar": [2, 3, 4]}, null]}

With the current approach, you will go like this:
remove /foo/3
remove /foo/2
add /foo/2
add /foo/1
replace /foo/3/id

where the replace will fail, because after removing /3, then /2, then adding /2, then adding /1, you should end up with:
left:

{"foo": [null, null, {"id": "BAZ_01", "bar": [0, 1]}, null, null]}

instead of
left:

{"foo": [null, null, null, {"id": "BAZ_01", "bar": [0, 1]}, null]}

I fixed it locally by changing the RemoveOperationComparer to manage remove/replace/add order and using it to reorder all operations, instead of only reordering the remove operations. Also, adding has to be ordered ascending and removing has to be descending to cover all cases.

I can provide code, but I first want to know if I am missing something because I'm actually not that comfortable with foreign code yet and I did this all midnight, so it's prone to errors, I guess.

Options ignored due to DeepEquals

The features supported in Options like ExcludePath and DiffBehaviors are effectively ignored since JToken.DeepEquals doesn't take those into account.

If you exclude a path that would show up in an DeepEquals array scan, DeepEquals will still return false, even if every other path is valid. The same thing for IgnoreMissingProperties or IgnoreNewProperties.

Assuming you're not interested in writing your own DeepEquals, building an interface around that so that users can write their own would be helpful.

Week comparison feature

Hi.

Have you thought about week comparison of the json?
For example :
expected: {"key1":"value1"}

given: {"key1":"value1", "key2":"value2"}
Would pass.

given: {"key1":"valueX", "key2":"value2"}
Would fail.

It basically wouldn't take in to account elements which are not in the expected string. If there is a need I could help you with that feature.

compare only keys

is it possible to compare 2 json objects and only compare the key names, rather than the values ?

for example

var a = { foo: 'bar', baz:true}
var b = { foo: 'boo', bingo:false}

if I compare b with a, I would like to get that baz has been added, bingo removed and ignore foo

in this event, I want b to end up being

{ foo: 'boo', baz:true }

Exception in FormatDeltaChildren

Hi,

I am trying to replicate the Visual Delta Formatter from the JsonDiffPatch Demo page using the default example data.

I have created my own formatter derived from BaseDeltaFormatter and I initate it using:

Recurse(context, delta, left, null, null, null, false);

This calls back into the Format override method and in here I check the type is Node and then call FormatDeltaChildren:

protected override void Format(DeltaType type, JsonFormatContext context, JToken delta, JToken leftValue, string key, string leftKey, MoveDestination movedFrom) { if (type == DeltaType.Node) FormatDeltaChildren(context, delta, leftValue); }

However, this throws an exception on the line:
Recurse(context, delta[key], left?[leftKey], key, leftKey, movedFrom, isLast);

Both key and leftKey are set to "0" but this doesn't exist in left.

The exception is "Accessed JArray values with invalid key value: "0". Int32 array index expected."

Thanks

Array Items not being diffed when item moves to end of array

An existing array item that gets moved to the end of an array does not get included in the diff results

Unit Test Code

protected readonly string _testDoc = @"{
                                              ""PartitionKey"": ""StoreFront.4aab3661-b697-4557-936f-1cd457328e5d"",
                                              ""id"": ""12345"",
                                              ""IsActive"": true,
                                              ""StoreDomain"": ""test.com"",
                                              ""Items"": [""test1"", ""test2""],
                                              ""LastModifiedOn"": ""2017-11-03T10:08:48.0532074-07:00"",
                                              ""LastModifiedBy"": ""test"",
                                              ""CreatedOn"": ""2017-11-03T10:08:48.0532074-07:00"",
                                              ""CreatedBy"": ""test"",
                                              ""Test"": {""FieldResponses"":[{""Key"":""PurchaserSSN"", ""Type"":""ga-secure-socialsecurity"",""Response"": ""333-33-3333"",""ResponseContext"":null}, {""Key"":""PurchaserID"", ""Type"":""ga-secure-textbox"",""Response"":""securetext"",""ResponseContext"":null}]}
                                            }";

        protected readonly string _changedDoc = @"{
                                              ""PartitionKey"": ""StoreFront.4aab3661-b697-4557-936f-1cd457328e5d"",
                                              ""id"": ""12345"",
                                              ""IsActive"": true,
                                              ""StoreDomain"": ""test.com"",
                                              ""Items"": [""test3"", ""test4"", ""test2""],
                                              ""LastModifiedOn"": ""2017-11-03T10:08:48.0532074-07:00"",
                                              ""LastModifiedBy"": ""test"",
                                              ""CreatedOn"": ""2017-11-03T10:08:48.0532074-07:00"",
                                              ""CreatedBy"": ""test"",
                                              ""Test"": {""FieldResponses"":[{""Key"":""PurchaserSSN"", ""Type"":""ga-secure-socialsecurity"",""Response"": ""555-55-5555"",""ResponseContext"":null}, {""Key"":""PurchaserID"", ""Type"":""ga-secure-textbox"",""Response"":""newValue"",""ResponseContext"":null}, {""Key"":""NewItem"", ""Type"":""ga-secure-textbox"",""Response"":""NewItemValue"",""ResponseContext"": {""test"": ""test""}}]}
                                            }";

        [Fact]
        public async Task TEST()
        {
            var d1 = CreateDocument(_testDoc);
            var d2 = CreateDocument(_changedDoc);
            var jdp = new JsonDiffPatch(new Options
            {
                ArrayDiff = ArrayDiffMode.Efficient,
                TextDiff = TextDiffMode.Simple
            });
            var diffObj = jdp.Diff(previousDocument, newDoc);
        }
		
		public JObject CreateDocument(string json)
        {
            return JObject.Parse(json);
        }

If you examine the output you can see it drops the "test2" item from the diff

2019-05-09_161731

If the "test2" is moved anywhere else i the array it is matched and a delta is created. Due to this Can not use this diff tool completely, will have to use this and write custom array diff code, unless a quick patch and push can be done to fix this issue.

Related to #14 ??

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.