JSON Patch and JSON Pointer: How "What Changed" Becomes a Standardized, Executable Sequence of Operations
"What's different between these two JSON documents" and "what's the smallest sequence of operations that transforms one into the other" are related but distinct questions β the second is what JSON Patch (RFC 6902) standardizes. Here's the six JSON Patch operations, the JSON Pointer path syntax they rely on, how the test operation enables optimistic concurrency control, and when the simpler JSON Merge Patch is sufficient instead.
By sadiqbd Β· June 13, 2026
When two JSON documents are "almost the same but not quite," the most useful question often isn't "what's different" but "what's the smallest set of operations that would turn one into the other" β and that question has a name: JSON Patch
The previous articles on this site covered structural diff fundamentals and practical diffing workflows. This article looks at a related but distinct concept: JSON Patch (RFC 6902) β a standardized format for describing changes to a JSON document as a sequence of operations β and how this connects to, and differs from, "diffing."
Diff output vs. patch operations: related, but not identical
A "diff" (as covered in previous articles) typically describes differences β "field X changed from A to B," "field Y was added," "field Z was removed" β in a human-readable (or tool-specific) format.
A JSON Patch document describes a sequence of operations that, when applied to one document, produce the other β using a standardized, machine-executable format:
[
{ "op": "replace", "path": "/status", "value": "active" },
{ "op": "remove", "path": "/temporaryFlag" },
{ "op": "add", "path": "/tags/-", "value": "new-tag" }
]
The relationship: a "diff" between two JSON documents can be expressed as a JSON Patch (a sequence of operations that transforms document A into document B) β JSON Patch is one possible output format for a diff, specifically one that's standardized and directly executable β rather than just descriptive.
The six JSON Patch operations
add: adds a value at the specified path β for objects, adds a new key; for arrays, inserts an element at the specified index (or appends, using - as the index, as shown in the example above).
remove: removes the value at the specified path.
replace: replaces the value at the specified path with a new value (equivalent to a remove followed by an add at the same path, but expressed as a single operation).
move: moves a value from one path to another (removing it from the source path, adding it at the destination path) β useful for reordering (within arrays) or restructuring (moving a value from one part of the object hierarchy to another) without needing to express this as a separate remove-and-add (which, for large values, would mean repeating the entire value in both the remove and add operations β move avoids this duplication).
copy: similar to move, but the source value remains (a copy is created at the destination, without removing the original).
test: doesn't modify anything β verifies that the value at the specified path matches an expected value, and fails the entire patch application if it doesn't. Useful as a precondition β e.g., "only apply this patch if /version currently equals 3" β preventing a patch from being applied to a document that's already changed in ways that would make the patch's other operations invalid/unintended.
JSON Pointer: the path syntax JSON Patch relies on
JSON Patch paths (/status, /tags/-, /items/0/name) use JSON Pointer syntax (RFC 6901) β a standardized way of referencing a specific location within a JSON document.
Basic syntax:
/represents the root of the document/keyreferences the value atkey(for an object)/array/0references the first element (index 0) ofarray/array/-(in some contexts, including JSON Patch'saddoperation specifically) represents "one past the end" of an array β i.e., appending- Nested paths:
/items/0/namereferences thenameproperty of the first element of theitemsarray
Special character escaping: JSON Pointer reserves / and ~ as structural characters within the pointer syntax itself β if a key name in your actual JSON contains a literal / or ~ character (unusual, but possible β e.g., a key like "a/b"), these must be escaped within the pointer: ~1 represents a literal /, and ~0 represents a literal ~ β without this escaping, a pointer couldn't distinguish between "a key containing a slash" and "a path separator indicating nesting."
Why "smallest set of operations" matters: bandwidth and atomicity
For large JSON documents where only a small portion changes β transmitting the full updated document (e.g., in an API request to update a resource) vs transmitting just a JSON Patch describing the changes represents a significant difference in payload size β a patch describing "change this one field" is vastly smaller than retransmitting an entire, mostly-unchanged, large document.
HTTP PATCH method (as distinct from PUT, which typically implies replacing an entire resource) is commonly associated with this kind of partial-update semantics β Content-Type: application/json-patch+json is the registered media type for requests whose body is a JSON Patch document β some APIs support receiving JSON Patch documents directly as the body of a PATCH request, applying the specified operations to the resource server-side.
Atomicity via test: the test operation enables a pattern sometimes called "optimistic concurrency control" β include a test operation checking that the resource's current version/state matches what the client expects (based on when the client last fetched the resource) β if, by the time the patch is applied server-side, the resource has already changed (e.g., another client modified it in the meantime) β the test operation fails, and (per the JSON Patch specification) the entire patch application fails (none of the subsequent operations are applied) β providing a mechanism to detect/prevent "lost update" scenarios without requiring separate, additional concurrency-control infrastructure.
JSON Merge Patch: a simpler (but less expressive) alternative
RFC 7396 defines "JSON Merge Patch" β a much simpler alternative to JSON Patch, for common, simpler update scenarios:
{
"status": "active",
"temporaryFlag": null
}
A JSON Merge Patch is itself just a JSON object/value β applying it means: for each key in the patch, set that key in the target document to the patch's value β except, a value of null in the patch means remove that key from the target.
Trade-offs vs JSON Patch:
- Simpler to construct and read β a Merge Patch looks like a "partial version" of the target document, which is often intuitive
- Less expressive β cannot express array element-level operations (insert/remove/reorder specific array elements) β a Merge Patch replaces an entire array wholesale if any part of it changes (there's no "change just element 3 of this array" in Merge Patch β the whole
arraykey would need to be included, with its complete, new value) - Cannot distinguish "set this field to
null" from "remove this field" β both are expressed identically in Merge Patch (anullvalue meaning "remove") β if a target document genuinely needs a field set tonull(as a meaningful value, distinct from the field being absent β recall the previous article's null-vs-missing discussion) β Merge Patch cannot express this βapplication/merge-patch+json'snullalways means "remove," with no way to express "set tonull" via Merge Patch specifically.
When to use which: Merge Patch for simple, object-level, no-array-surgery, no-meaningful-null-values update scenarios (often sufficient for many configuration/settings-style updates); JSON Patch when array-element-level operations, meaningful null values, or atomicity/test-operation guarantees are needed.
How to use the JSON Diff tool on sadiqbd.com
- For understanding "what changed": the structural diff output (covered in previous articles) describes differences in a human-readable way β useful for review/debugging
- For generating an applicable patch: if your workflow requires a JSON Patch (RFC 6902) document β describing the diff as a sequence of operations suitable for
PATCHrequests β some diff tools can output in this format directly; where this isn't directly supported, the structural diff output provides the information needed to manually construct the corresponding JSON Patch operations - For Merge Patch scenarios: if your target API uses
application/merge-patch+jsonβ constructing the patch is often simpler: it's just "the fields that changed, plusnullfor any fields that should be removed" β which a structural diff's "added/changed/removed" output maps to fairly directly (with the caveat, as discussed, that array-level changes require including the entire new array, not element-level patches)
Frequently Asked Questions
Does JSON Patch guarantee the operations are applied in a way that "looks like" a minimal diff to a human?
Not necessarily β JSON Patch describes operations that achieve the transformation, but multiple different sequences of operations could achieve the same final result β a patch-generation tool might produce a technically-correct but not-maximally-"minimal"-or-intuitive sequence (e.g., expressing a "value changed" as separate remove then add operations, rather than a single replace) β while replace exists specifically for the "value changed" case and is generally preferred where applicable, the specification itself doesn't mandate one "canonical" patch for any given transformation β different tools may produce different, equally-valid patches for the same underlying diff.
Is the JSON Diff tool free? Yes β completely free, no sign-up required.
Try the JSON Diff tool free at sadiqbd.com β compare JSON documents and identify exactly what changed between versions.