|
1 | 1 | # system-text-json-jsondiffpatch
|
2 | 2 |
|
3 |
| -High-performance, low-allocating JSON objects diff and patch extension for `System.Text.Json`. |
| 3 | +High-performance, low-allocating JSON objects diff and patch extension for System.Text.Json. |
4 | 4 |
|
5 | 5 | ## Features
|
6 | 6 |
|
7 | 7 | - Use [jsondiffpatch](https://github.com/benjamine/jsondiffpatch) delta format described [here](https://github.com/benjamine/jsondiffpatch/blob/master/docs/deltas.md)
|
8 |
| -- Target latest `.NET Standard` and `.NET Framework 4.6.1` (for legacy apps) and leverage latest .NET features |
9 |
| -- Alternative to [jsondiffpatch.net](https://github.com/wbish/jsondiffpatch.net) which is based on `Newtonsoft.Json` |
10 | 8 | - Support generating patch document in RFC 6902 JSON Patch format
|
11 |
| -- Fast large JSON document diffing with less memory consumption |
| 9 | +- Target latest .NET Standard and .NET Framework 4.6.1 (for legacy apps) and leverage latest .NET features |
| 10 | +- Alternative to [jsondiffpatch.net](https://github.com/wbish/jsondiffpatch.net) which is based on Newtonsoft.Json |
| 11 | +- Fast large JSON document diffing with less memory consumption (see [benchmark](https://github.com/weichch/system-text-json-jsondiffpatch/blob/main/Benchmark.md)) |
12 | 12 | - Support smart array diffing (e.g. move detect) using LCS and custom array item matcher
|
13 | 13 | - _(Only when not using RFC 6902 format)_ Support diffing long text using [google-diff-match-patch](http://code.google.com/p/google-diff-match-patch/), or write your own diff algorithm
|
14 |
| -- `JsonNode.DeepClone` and `JsonNode.DeepEquals` methods |
| 14 | +- Bonus `JsonNode.DeepClone` and `JsonNode.DeepEquals` methods |
| 15 | +- JSON assert for xUnit, MSTest v2 and NUnit with customizable delta output |
15 | 16 |
|
16 |
| -# Install |
| 17 | +## Install |
17 | 18 |
|
18 |
| -Install from [NuGet.org](https://www.nuget.org/packages/SystemTextJson.JsonDiffPatch/): |
19 |
| - |
20 |
| -``` |
21 |
| -Install-Package SystemTextJson.JsonDiffPatch |
22 |
| -``` |
| 19 | +| JsonDiffPatch | xUnit Assert | MSTest v2 Assert | NUnit Assert | |
| 20 | +|------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------| |
| 21 | +| [](https://www.nuget.org/packages/SystemTextJson.JsonDiffPatch/) | [](https://www.nuget.org/packages/SystemTextJson.JsonDiffPatch.Xunit/) | [](https://www.nuget.org/packages/SystemTextJson.JsonDiffPatch.MSTest/) | [](https://www.nuget.org/packages/SystemTextJson.JsonDiffPatch.NUnit/) | |
23 | 22 |
|
24 | 23 | ## Usage
|
25 | 24 | ### Diff
|
@@ -66,64 +65,65 @@ bool equal = node1.DeepEquals(node2);
|
66 | 65 | var node1 = JsonNode.Parse(...);
|
67 | 66 | var node2 = JsonNode.Parse(...);
|
68 | 67 | JsonNode? diff = node1.Diff(node2);
|
69 |
| -// Patch |
| 68 | +// In-place patch |
70 | 69 | JsonDiffPatcher.Patch(ref node1, diff);
|
71 |
| -// Unpatch |
| 70 | +// Clone & patch |
| 71 | +node1.PatchNew(diff); |
| 72 | +// In-place unpatch |
72 | 73 | JsonDiffPatcher.ReversePatch(ref node1, diff);
|
| 74 | +// Clone & unpatch |
| 75 | +node1.ReversePatchNew(diff); |
73 | 76 | ```
|
74 | 77 |
|
75 |
| -## Benchmark |
76 |
| - |
77 |
| -Benchmarks were 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/). |
78 |
| - |
79 |
| -### Demo JSON object from `jsondiffpatch` |
80 |
| - |
81 |
| -``` ini |
82 |
| - |
83 |
| -BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1415 (21H1/May2021Update) |
84 |
| -11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores |
85 |
| -.NET SDK=6.0.200 |
86 |
| - [Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT |
87 |
| - DefaultJob : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT |
88 |
| - |
| 78 | +### Assert (Unit Testing) |
89 | 79 |
|
| 80 | +```csharp |
| 81 | +var expected = JsonNode.Parse(...); |
| 82 | +var actual = JsonNode.Parse(...); |
| 83 | + |
| 84 | +// xUnit |
| 85 | +JsonAssert.Equal(expected, actual); |
| 86 | +actual.ShouldEqual(expected); |
| 87 | +JsonAssert.NotEqual(expected, actual); |
| 88 | +actual.ShouldNotEqual(expected); |
| 89 | + |
| 90 | +// MSTest |
| 91 | +JsonAssert.AreEqual(expected, actual); |
| 92 | +Assert.That.JsonAreEqual(expected, actual); |
| 93 | +JsonAssert.AreNotEqual(expected, actual); |
| 94 | +Assert.That.JsonAreNotEqual(expected, actual); |
| 95 | + |
| 96 | +// NUnit |
| 97 | +JsonAssert.AreEqual(expected, actual); |
| 98 | +Assert.That(actual, JsonIs.EqualTo(expected)); |
| 99 | +JsonAssert.AreNotEqual(expected, actual); |
| 100 | +Assert.That(actual, JsonIs.NotEqualTo(expected)); |
90 | 101 | ```
|
91 |
| -| Method | Mean | Min | Max | P95 | P80 | Allocated | |
92 |
| -|-------------------------- |----------:|----------:|----------:|----------:|----------:|----------:| |
93 |
| -| Diff_JsonNet | 107.55 μs | 95.29 μs | 122.73 μs | 118.01 μs | 113.43 μs | 132 KB | |
94 |
| -| Diff_SystemTextJson | 91.79 μs | 75.05 μs | 126.17 μs | 112.61 μs | 101.81 μs | 70 KB | |
95 |
| -| Diff_JsonNet_Rfc | 130.02 μs | 115.41 μs | 156.85 μs | 151.16 μs | 136.42 μs | 150 KB | |
96 |
| -| Diff_SystemTextJson_Rfc | 106.12 μs | 95.75 μs | 120.03 μs | 115.57 μs | 110.23 μs | 93 KB | |
97 |
| -| Patch_JsonNet | 116.92 μs | 107.06 μs | 137.86 μs | 133.26 μs | 122.34 μs | 162 KB | |
98 |
| -| Patch_SystemTextJson | 45.05 μs | 37.98 μs | 56.60 μs | 55.13 μs | 47.42 μs | 37 KB | |
99 |
| -| DeepEquals_JsonNet | 69.14 μs | 62.38 μs | 76.96 μs | 74.78 μs | 71.95 μs | 91 KB | |
100 |
| -| DeepEquals_SystemTextJson | 53.43 μs | 47.96 μs | 65.03 μs | 60.30 μs | 56.35 μs | 40 KB | |
101 |
| -| DeepClone_JsonNet | 51.28 μs | 44.65 μs | 62.34 μs | 58.69 μs | 54.52 μs | 70 KB | |
102 |
| -| DeepClone_SystemTextJson | 47.82 μs | 38.78 μs | 59.26 μs | 56.94 μs | 51.11 μs | 45 KB | |
103 |
| - |
104 |
| - |
105 |
| -### Large JSON object |
106 |
| - |
107 |
| -``` ini |
108 |
| - |
109 |
| -BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1415 (21H1/May2021Update) |
110 |
| -11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores |
111 |
| -.NET SDK=6.0.200 |
112 |
| - [Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT |
113 |
| - DefaultJob : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT |
114 |
| - |
115 | 102 |
|
| 103 | +Example output _(when output is enabled)_: |
| 104 | +``` |
| 105 | +JsonAssert.Equal() failure. |
| 106 | +Expected: |
| 107 | +{ |
| 108 | + "foo": "baz" |
| 109 | +} |
| 110 | +Actual: |
| 111 | +{ |
| 112 | + "foo": "bar", |
| 113 | + "baz": "qux" |
| 114 | +} |
| 115 | +Delta: |
| 116 | +{ |
| 117 | + "foo": [ |
| 118 | + "baz", |
| 119 | + "bar" |
| 120 | + ], |
| 121 | + "baz": [ |
| 122 | + "qux" |
| 123 | + ] |
| 124 | +} |
116 | 125 | ```
|
117 |
| -| Method | Mean | Min | Max | P95 | P80 | Allocated | |
118 |
| -|-------------------------- |----------:|----------:|----------:|----------:|----------:|----------:| |
119 |
| -| Diff_JsonNet | 11.155 ms | 9.132 ms | 13.732 ms | 12.802 ms | 11.896 ms | 4 MB | |
120 |
| -| Diff_SystemTextJson | 8.375 ms | 7.334 ms | 9.132 ms | 8.978 ms | 8.690 ms | 3 MB | |
121 |
| -| Diff_JsonNet_Rfc | 12.774 ms | 9.807 ms | 17.990 ms | 17.479 ms | 13.374 ms | 6 MB | |
122 |
| -| Diff_SystemTextJson_Rfc | 11.664 ms | 10.341 ms | 12.918 ms | 12.668 ms | 12.142 ms | 5 MB | |
123 |
| -| Patch_JsonNet | 12.146 ms | 10.344 ms | 13.576 ms | 13.302 ms | 12.647 ms | 5 MB | |
124 |
| -| Patch_SystemTextJson | 4.693 ms | 2.422 ms | 5.685 ms | 5.412 ms | 5.061 ms | 2 MB | |
125 |
| -| DeepEquals_JsonNet | 4.164 ms | 2.584 ms | 5.424 ms | 4.917 ms | 4.652 ms | 2 MB | |
126 |
| -| DeepEquals_SystemTextJson | 3.254 ms | 2.071 ms | 3.837 ms | 3.709 ms | 3.499 ms | 2 MB | |
127 |
| -| DeepClone_JsonNet | 3.997 ms | 2.529 ms | 5.165 ms | 4.772 ms | 4.531 ms | 2 MB | |
128 |
| -| DeepClone_SystemTextJson | 3.396 ms | 2.085 ms | 3.981 ms | 3.811 ms | 3.609 ms | 2 MB | |
129 | 126 |
|
| 127 | +## Benchmark |
| 128 | + |
| 129 | +[Benchmark results](https://github.com/weichch/system-text-json-jsondiffpatch/blob/main/Benchmark.md) were 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/). |
0 commit comments