Skip to content

Commit 4bf5093

Browse files
authored
Add Patch and Unpatch (#2)
Add patch and unpatch
1 parent a0225fa commit 4bf5093

27 files changed

+1116
-296
lines changed

README.md

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# system-text-json-jsondiffpath
1+
# system-text-json-jsondiffpatch
22

33
High-performance, low-allocating JSON objects diff and patch extension for `System.Text.Json`.
44

@@ -10,9 +10,9 @@ High-performance, low-allocating JSON objects diff and patch extension for `Syst
1010
- Fast large JSON document diffing with less memory consumption
1111
- Support smart array diffing (e.g. move detect) using LCS and custom array item matcher
1212
- Support diffing long text using [google-diff-match-patch](http://code.google.com/p/google-diff-match-patch/), or write your own diff algorithm
13-
- `JsonNode.Clone` and `JsonNode.DeepEquals` methods
13+
- `JsonNode.DeepClone` and `JsonNode.DeepEquals` methods
1414

15-
- (_Under development_) Patch, unpatch, formatters etc
15+
- (_Under development_) formatters etc
1616

1717
# Install
1818

@@ -27,27 +27,27 @@ Install-Package SystemTextJson.JsonDiffPatch
2727

2828
```csharp
2929
// Diff JSON files
30-
JsonDocument? diff = JsonDiffPatcher.DiffFile(file1, file2);
30+
JsonNode? diff = JsonDiffPatcher.DiffFile(file1, file2);
3131
// Diff Span<byte>
32-
JsonDocument? diff = JsonDiffPatcher.Diff(span1, span2);
32+
JsonNode? diff = JsonDiffPatcher.Diff(span1, span2);
3333
// Diff streams
34-
JsonDocument? diff = JsonDiffPatcher.Diff(stream1, stream2);
34+
JsonNode? diff = JsonDiffPatcher.Diff(stream1, stream2);
3535
// Diff JSON strings
36-
JsonDocument? diff = JsonDiffPatcher.Diff(json1, json2);
36+
JsonNode? diff = JsonDiffPatcher.Diff(json1, json2);
3737
// Diff JSON readers
38-
JsonDocument? diff = JsonDiffPatcher.Diff(ref reader1, ref reader2);
38+
JsonNode? diff = JsonDiffPatcher.Diff(ref reader1, ref reader2);
3939

4040
// Diff mutable JsonNode objects
4141
var node1 = JsonNode.Parse(...);
4242
var node2 = JsonNode.Parse(...);
4343
JsonNode? diff = node1.Diff(node2);
4444
```
4545

46-
### Clone
46+
### DeepClone
4747

4848
```csharp
4949
var node = JsonNode.Parse(...);
50-
JsonNode? cloned = node.Clone();
50+
JsonNode? cloned = node.DeepClone();
5151
```
5252

5353
### DeepEquals
@@ -58,6 +58,18 @@ var node2 = JsonNode.Parse(...);
5858
bool equal = node1.DeepEquals(node2);
5959
```
6060

61+
### Patch & Unpatch
62+
63+
```csharp
64+
var node1 = JsonNode.Parse(...);
65+
var node2 = JsonNode.Parse(...);
66+
JsonNode? diff = node1.Diff(node2);
67+
// Patch
68+
JsonDiffPatcher.Patch(ref node1, diff);
69+
// Unpatch
70+
JsonDiffPatcher.ReversePatch(ref node1, diff);
71+
```
72+
6173
### Options
6274

6375
```csharp
@@ -68,14 +80,6 @@ public struct JsonDiffOptions
6880
/// </summary>
6981
public bool SuppressDetectArrayMove { get; set; }
7082

71-
/// <summary>
72-
/// Specifies whether to include moved item value. Default value is <c>false</c>.
73-
/// </summary>
74-
/// <remarks>
75-
/// See <see link="https://github.com/benjamine/jsondiffpatch/blob/master/docs/deltas.md#array-moves"/>.
76-
/// </remarks>
77-
public bool IncludeValueOnMove { get; set; }
78-
7983
/// <summary>
8084
/// Gets or sets the function to match array items.
8185
/// </summary>
@@ -148,5 +152,4 @@ Intel Core i7-10750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
148152
| LargeObject_Mutable | 6,699.4 μs | 6,400.8 μs | 7,017.8 μs | 6,935.1 μs | 6,804.6 μs | 3,538 KB |
149153

150154

151-
_\* Generated using example objects [here](https://github.com/weichch/system-text-json-jsondiffpath/tree/main/test/Examples) and benchmark tests [here](https://github.com/weichch/system-text-json-jsondiffpath/tree/main/test/SystemTextJson.JsonDiffPatch.Benchmark/SimpleDiffBenchmark.cs)_
152-
155+
_\* Generated using example objects [here](https://github.com/weichch/system-text-json-jsondiffpatch/tree/main/test/Examples) and benchmark tests [here](https://github.com/weichch/system-text-json-jsondiffpatch/tree/main/test/SystemTextJson.JsonDiffPatch.Benchmark/SimpleDiffBenchmark.cs)_
Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
using System.Text.Json.Nodes;
2-
3-
namespace System.Text.Json.Diffs
1+
namespace System.Text.Json.JsonDiffPatch.Diffs
42
{
53
/// <summary>
64
/// Defines a function that determines whether two items in arrays are equal.
75
/// </summary>
8-
/// <param name="x">The element in array1.</param>
9-
/// <param name="indexX">The index of <paramref name="x"/> in array1.</param>
10-
/// <param name="y">The element in array2.</param>
11-
/// <param name="indexY">The index of <paramref name="y"/> in array2.</param>
12-
public delegate bool ArrayItemMatch(JsonNode? x, int indexX,
13-
JsonNode? y, int indexY, out bool deepEqual);
6+
/// <param name="context">The comparison context.</param>
7+
public delegate bool ArrayItemMatch(ref ArrayItemMatchContext context);
148
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System.Text.Json.Nodes;
2+
3+
namespace System.Text.Json.JsonDiffPatch.Diffs
4+
{
5+
/// <summary>
6+
/// Context for matching array items.
7+
/// </summary>
8+
public struct ArrayItemMatchContext
9+
{
10+
/// <summary>
11+
/// Creates an instance of the context.
12+
/// </summary>
13+
/// <param name="left">The left item.</param>
14+
/// <param name="leftPos">The index of the left item.</param>
15+
/// <param name="right">The right item.</param>
16+
/// <param name="rightPos">The index of the right item.</param>
17+
public ArrayItemMatchContext(JsonNode? left, int leftPos, JsonNode? right, int rightPos)
18+
{
19+
Left = left;
20+
LeftPosition = leftPos;
21+
Right = right;
22+
RightPosition = rightPos;
23+
IsDeepEqual = false;
24+
}
25+
26+
/// <summary>
27+
/// Gets the left item.
28+
/// </summary>
29+
public JsonNode? Left { get; }
30+
31+
/// <summary>
32+
/// Gets the index of the left item.
33+
/// </summary>
34+
public int LeftPosition { get; }
35+
36+
/// <summary>
37+
/// Gets the right item.
38+
/// </summary>
39+
public JsonNode? Right { get; }
40+
41+
/// <summary>
42+
/// Gets the index of the right item.
43+
/// </summary>
44+
public int RightPosition { get; }
45+
46+
/// <summary>
47+
/// Gets whether the result was the two items are deeply equal.
48+
/// </summary>
49+
public bool IsDeepEqual { get; private set; }
50+
51+
/// <summary>
52+
/// Sets <see cref="IsDeepEqual"/> to <c>true</c>.
53+
/// </summary>
54+
public void DeepEqual()
55+
{
56+
IsDeepEqual = true;
57+
}
58+
}
59+
}

src/SystemTextJson.JsonDiffPatch/Diffs/DefaultArrayItemComparer.cs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,45 @@
11
using System.Text.Json.Nodes;
22

3-
namespace System.Text.Json.Diffs
3+
namespace System.Text.Json.JsonDiffPatch.Diffs
44
{
55
internal readonly struct DefaultArrayItemComparer
66
{
7-
private readonly JsonDiffOptionsView _options;
7+
private readonly JsonDiffOptions _options;
88

9-
public DefaultArrayItemComparer(in JsonDiffOptionsView options)
9+
public DefaultArrayItemComparer(JsonDiffOptions options)
1010
{
1111
_options = options;
1212
}
1313

14-
public bool MatchArrayItem(
15-
JsonNode? x, int indexX, JsonNode? y, int indexY,
16-
out bool deepEqual)
14+
public bool MatchArrayItem(ref ArrayItemMatchContext context)
1715
{
1816
// Returns if one JSON node matches another. Nodes are considered match if:
1917
// - they are deeply equal, OR
2018
// - they are of JavaScript object type JsonObject and JsonArray and their
2119
// positions in corresponding arrays are equal
2220
// See: https://github.com/benjamine/jsondiffpatch/blob/a8cde4c666a8a25d09d8f216c7f19397f2e1b569/src/filters/arrays.js#L43
2321

24-
deepEqual = false;
25-
2622
if (_options.PreferFuzzyArrayItemMatch
27-
&& x is JsonObject or JsonArray
28-
&& y is JsonObject or JsonArray)
23+
&& context.Left is JsonObject or JsonArray
24+
&& context.Right is JsonObject or JsonArray)
2925
{
30-
if (FuzzyMatchItem(x, indexX, y, indexY,
31-
out var fuzzyResult, out deepEqual))
26+
if (FuzzyMatchItem(ref context, out var fuzzyResult))
3227
{
3328
return fuzzyResult;
3429
}
3530
}
3631

37-
if (x.DeepEquals(y))
32+
if (context.Left.DeepEquals(context.Right))
3833
{
39-
deepEqual = true;
34+
context.DeepEqual();
4035
return true;
4136
}
4237

4338
if (!_options.PreferFuzzyArrayItemMatch
44-
&& x is JsonObject or JsonArray
45-
&& y is JsonObject or JsonArray)
39+
&& context.Left is JsonObject or JsonArray
40+
&& context.Right is JsonObject or JsonArray)
4641
{
47-
if (FuzzyMatchItem(x, indexX, y, indexY,
48-
out var fuzzyResult, out deepEqual))
42+
if (FuzzyMatchItem(ref context, out var fuzzyResult))
4943
{
5044
return fuzzyResult;
5145
}
@@ -54,18 +48,15 @@ public bool MatchArrayItem(
5448
return false;
5549
}
5650

57-
private bool FuzzyMatchItem(JsonNode? x, int indexX,
58-
JsonNode? y, int indexY,
59-
out bool result, out bool deepEqual)
51+
private bool FuzzyMatchItem(ref ArrayItemMatchContext context, out bool result)
6052
{
6153
result = false;
62-
deepEqual = false;
6354

6455
var keyFinder = _options.ArrayObjectItemKeyFinder;
6556
if (keyFinder is not null)
6657
{
67-
var keyX = keyFinder(x, indexX);
68-
var keyY = keyFinder(y, indexY);
58+
var keyX = keyFinder(context.Left, context.LeftPosition);
59+
var keyY = keyFinder(context.Right, context.RightPosition);
6960

7061
if (keyX is null && keyY is null)
7162
{
@@ -79,7 +70,7 @@ private bool FuzzyMatchItem(JsonNode? x, int indexX,
7970

8071
if (_options.ArrayObjectItemMatchByPosition)
8172
{
82-
if (indexX == indexY)
73+
if (context.LeftPosition == context.RightPosition)
8374
{
8475
result = true;
8576
return true;

0 commit comments

Comments
 (0)