RaJsonDiffPatch 26.9.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package RaJsonDiffPatch --version 26.9.2
                    
NuGet\Install-Package RaJsonDiffPatch -Version 26.9.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RaJsonDiffPatch" Version="26.9.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RaJsonDiffPatch" Version="26.9.2" />
                    
Directory.Packages.props
<PackageReference Include="RaJsonDiffPatch" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RaJsonDiffPatch --version 26.9.2
                    
#r "nuget: RaJsonDiffPatch, 26.9.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RaJsonDiffPatch@26.9.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RaJsonDiffPatch&version=26.9.2
                    
Install as a Cake Addin
#tool nuget:?package=RaJsonDiffPatch&version=26.9.2
                    
Install as a Cake Tool

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

  • test operations compared the found value against the whole document, so every test failed.
  • 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.UnescapeDataString in the pointer decoder corrupted property names containing %.
  • Root-level paths, out-of-range array indices, unknown op names and non-array patch documents raised NullReferenceException or silently did nothing; they now report what is wrong.
  • move rejected /a → /ab as "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

  • add to 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, copy or test. It produces add, remove and replace only. 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.
  • test compares with JToken.DeepEquals: object member order is not significant, but numbers are compared by JSON type, so 1 does not equal 1.0. The differ is stricter still — it compares scalars by their textual form, so it reports 1 → 1.0 as a replace.

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.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
26.9.3 122 9/2/2026
26.9.2 93 9/1/2026