RaJsonDiffPatch 26.9.2
See the version list below for details.
dotnet add package RaJsonDiffPatch --version 26.9.2
NuGet\Install-Package RaJsonDiffPatch -Version 26.9.2
<PackageReference Include="RaJsonDiffPatch" Version="26.9.2" />
<PackageVersion Include="RaJsonDiffPatch" Version="26.9.2" />
<PackageReference Include="RaJsonDiffPatch" />
paket add RaJsonDiffPatch --version 26.9.2
#r "nuget: RaJsonDiffPatch, 26.9.2"
#:package RaJsonDiffPatch@26.9.2
#addin nuget:?package=RaJsonDiffPatch&version=26.9.2
#tool nuget:?package=RaJsonDiffPatch&version=26.9.2
RaJsonDiffPatch
A .NET Standard 2.0 implementation of JSON Patch (RFC 6902)
and JSON Pointer (RFC 6901) for Json.NET (JToken) documents,
plus a diff generator that produces a patch from two documents.
dotnet add package RaJsonDiffPatch
Namespaces
using JsonDiffPatch; // PatchDocument, JsonDiffer, JsonPatcher, the operations
using Tavis; // JsonPointer
Drop-in replacement for the original JsonDiffPatch package. The namespaces and the public API
are unchanged, so swapping the package reference needs no source edits — you just get the fixes
below. Only the package id and assembly are renamed. Don't reference both packages at once: the
duplicated type names would be ambiguous.
Fixed relative to the original
testoperations compared the found value against the whole document, so everytestfailed.- JSON Pointer escaping was applied inconsistently — property names containing
/or~produced invalid pointers, and a generated patch could not be re-parsed and re-applied. Uri.UnescapeDataStringin the pointer decoder corrupted property names containing%.- Root-level paths, out-of-range array indices, unknown
opnames and non-array patch documents raisedNullReferenceExceptionor silently did nothing; they now report what is wrong. moverejected/a→/abas "below from path" because the check was a string prefix test.
Diffing two documents
var left = JToken.Parse(@"{ ""a"": 1, ""b"": [1, 2, 3] }");
var right = JToken.Parse(@"{ ""a"": 2, ""b"": [1, 2, 3, 4] }");
var patch = new JsonDiffer().Diff(left, right, useIdPropertyToDetermineEquality: false);
// [{"op":"replace","path":"/a","value":2},{"op":"add","path":"/b/3","value":4}]
Console.WriteLine(patch.ToString(Formatting.None));
Pass useIdPropertyToDetermineEquality: true to match objects inside arrays by their id property
rather than by deep equality. Reordered or edited elements are then diffed against the element with
the same id, instead of being reported as a wholesale replacement.
Applying a patch
Patch takes the document by reference, because an operation on the root replaces it outright.
JToken target = JToken.Parse(@"{ ""foo"": ""bar"" }");
var patch = PatchDocument.Parse(@"[{ ""op"": ""add"", ""path"": ""/baz"", ""value"": ""qux"" }]");
new JsonPatcher().Patch(ref target, patch);
// {"foo":"bar","baz":"qux"}
Building a patch by hand
Operations are immutable; the path always comes first.
var patch = new PatchDocument(
new TestOperation(new JsonPointer("/a/b/c"), new JValue("foo")),
new RemoveOperation(new JsonPointer("/a/b/c")),
new AddOperation(new JsonPointer("/a/b/c"), new JArray(new JValue("foo"), new JValue("bar"))),
new ReplaceOperation(new JsonPointer("/a/b/c"), new JValue(42)),
new MoveOperation(new JsonPointer("/a/b/d"), new JsonPointer("/a/b/c")),
new CopyOperation(new JsonPointer("/a/b/e"), new JsonPointer("/a/b/d")));
Reading and writing the wire format
var patch = PatchDocument.Parse(json); // from a string
var patch = PatchDocument.Load(stream); // from a stream (left open)
var patch = PatchDocument.Load(jArray); // from an already-parsed JArray
string json = patch.ToString(Formatting.None); // to a string
Stream bytes = patch.ToStream(); // to a new MemoryStream
patch.CopyToStream(existingStream); // to a stream you own
Pointers
JsonPointer handles RFC 6901 escaping, so property names containing / or ~ round-trip
correctly. Tokens are held decoded and re-escaped on ToString().
new JsonPointer("/a~1b").Find(doc); // the property literally named "a/b"
new JsonPointer("/a~0b").Find(doc); // the property literally named "a~b"
new JsonPointer("").Find(doc); // the whole document
new JsonPointer("/").Find(doc); // the property named ""
JsonPointer.Encode("a/b"); // "a~1b"
A pointer that cannot be resolved throws ArgumentException naming the segment that failed:
Failed to dereference pointer '/books/9/title' at segment 2 ('9'): after '/books', '9' is not a valid index into the array of 0 element(s).
Behaviour worth knowing
addto a path holding an array appends to it rather than replacing the array. RFC 6902 specifies replacement; this library has always appended, and the behaviour is kept for compatibility. Target/the/array/-for an explicit append, or an index to insert.- The differ does not emit
move,copyortest. It producesadd,removeandreplaceonly. Array edits that change the length are expressed as a removal of the changed span followed by additions, so the patch is correct but not minimal. testcompares withJToken.DeepEquals: object member order is not significant, but numbers are compared by JSON type, so1does not equal1.0. The differ is stricter still — it compares scalars by their textual form, so it reports1→1.0as areplace.
Targets
netstandard2.0 — .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+, Mono, Xamarin, Unity.
The only dependency is Newtonsoft.Json 13.0.3.
Credits and licence
Apache Licence 2.0 — see License.txt.
- Forked from mcintyre321/JsonDiffPatch
- JSON Pointer from tavis-software/Tavis.JsonPointer
- Patch operations from tavis-software/Tavis.JsonPatch
- Diff generator by Ian Mercer
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Newtonsoft.Json (>= 13.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.