Fundamentals
JSON Pointer and JSON Patch — Worked Example
Copy a JSON Pointer path and understand all six JSON Patch operations with a starting document and a reproducible result.
JSON Pointer identifies a value by its path. JSON Patch describes a sequence of changes using those paths. JSONPath serves a different purpose: selecting sets of values with queries.
Try the same document in the visualizer
Open the JSON visualizer, choose Sample, and select a node. Copy Pointer returns its path; Copy JSON value returns its contents. The graph is a bounded view; copies preserve the complete value.
Starting document:
{
"users": [
{
"id": 1,
"name": "Ana"
},
{
"id": 2,
"name": "Ben"
}
],
"a/b~c": {
"": "value"
},
"version": 1
}
Paths to check
Scroll horizontally to see all columns.
| Pointer (shown as a JSON string) | Value |
|---|---|
"" |
Whole document |
"/users/0/name" |
"Ana" |
"/users/1/id" |
2 |
"/a~1b~0c/" |
"value" |
The last path includes a key containing / and ~, followed by an empty key. Escape each segment by replacing ~ with ~0, then / with ~1. Decode in the reverse order: ~1, then ~0. This makes ~01 refer to the literal key ~1.
"" differs from "/": the latter selects an empty-name property at the root. Array indices start at zero and cannot contain leading zeroes: /users/01 is invalid for that array. The - token does not resolve an existing element; JSON Patch uses it for appending.
This is the string form of RFC 6901. A URI fragment such as #/users/0/name also requires appropriate URI encoding; do not add # to JSON Patch path fields. When representing a Pointer inside JSON, apply JSON string escaping to quotation marks and backslashes too.
A complete Patch: six operations and the result
Apply this array to the starting document above, in order:
[
{
"op": "test",
"path": "/version",
"value": 1
},
{
"op": "replace",
"path": "/users/0/name",
"value": "Alicia"
},
{
"op": "add",
"path": "/users/-",
"value": {
"id": 3,
"name": "Cyr"
}
},
{
"op": "copy",
"from": "/a~1b~0c/",
"path": "/snapshot"
},
{
"op": "move",
"from": "/users/1",
"path": "/users/0"
},
{
"op": "remove",
"path": "/users/2"
},
{
"op": "replace",
"path": "/version",
"value": 2
}
]
testchecks the version before changing anything.replacechanges Ana’s name.addappends Cyr to the array.copycopies the empty-key value tosnapshot.moveremoves Ben from his position and inserts him at the beginning.removedeletes Cyr, who is still at index 2.- The final
replaceupdates the version.
Expected result:
{
"users": [
{
"id": 2,
"name": "Ben"
},
{
"id": 1,
"name": "Alicia"
}
],
"a/b~c": {
"": "value"
},
"version": 2,
"snapshot": "value"
}
Each operation sees the result of the preceding one; indices shift on insertion or removal. add can replace an existing object member, but inserts into arrays. Its parent must exist. replace and remove require an existing target. copy and move require from; moving a node into its own descendant is invalid. These rules are defined by RFC 6902.
Errors and atomicity
Changing the first test to value: 99 makes this example fail before changing anything. That does not guarantee rollback in every library: an in-place application can fail after earlier operations have already changed data. Work on a copy and commit the result only when all operations succeed.
HTTP PATCH requires atomic application. For an API, send Content-Type: application/json-patch+json when the server supports that format. If-Match with the received ETag helps avoid overwriting a different version; the server must enforce it. A 412 indicates a failed HTTP precondition, not every possible JSON Patch error.
When to use Merge Patch
JSON Merge Patch, RFC 7396, uses application/merge-patch+json. Within an object, null removes a property instead of setting its value to null. Arrays are replaced as a whole, with no index operations. A non-object patch replaces the entire document, even when that patch is null.
For field updates this can be simpler; use JSON Patch when setting a property to null or editing array positions matters.
Continue checking
Compare the starting document and result in JSON Diff. To select multiple matches, use JSONPath. Neither tool applies this guide’s Patch; check the mutation options and behavior of your chosen library. A patch generator is not guaranteed to produce the smallest possible patch.
Related tools
Frequently asked questions
Is the root pointer /?
No. The root is the empty string. / selects a property whose name is empty.
Does the visualizer apply JSON Patch?
No. It explores JSON and copies paths and values. This guide shows operations and their result; JSON Diff compares documents and does not generate or apply a Patch.
Does a library roll back changes after an error?
Do not assume so. HTTP PATCH requires atomic application, but an in-place library may leave partial changes. Apply to a copy and commit only after all operations succeed.