JSON Patch
JSON Patch is a standardized format for expressing a sequence of operations to apply to a JavaScript Object Notation (JSON) document, enabling partial modifications without requiring the full document to be resent.[1] Defined in RFC 6902 by the Internet Engineering Task Force (IETF), it uses the media type "application/json-patch+json" and is particularly suited for the HTTP PATCH method to perform atomic updates on resources represented as JSON.[1]
The core structure of a JSON Patch document is an array of JSON objects, each specifying an operation with required members "op" (indicating the operation type) and "path" (a JSON Pointer per RFC 6901 to locate the target within the document).[1] Supported operations include add (to insert a value at a specified location or replace if it exists), remove (to delete a value), replace (to swap a value with a new one), move (to relocate a value from one path to another), copy (to duplicate a value to a new path), and test (to verify if a value matches an expected one before proceeding).[1] Operations are applied sequentially to the target JSON document; if any fails (e.g., due to a missing path or test mismatch), processing halts and the entire patch is considered unsuccessful, ensuring reliable partial updates for APIs and data synchronization.[1]
JSON Patch addresses limitations in earlier JSON handling by providing a precise, reversible way to describe changes, making it widely adopted in web services, content management systems, and protocols requiring efficient data evolution.[1] It complements related standards like JSON Merge Patch (RFC 7386), which uses a simpler merge-based approach, but JSON Patch offers finer-grained control through explicit operations.[2]
Overview
Definition and Purpose
JSON Patch is a standardized format defined by the Internet Engineering Task Force (IETF) in Request for Comments (RFC) 6902, published in April 2013, for expressing a sequence of operations to apply to a target JavaScript Object Notation (JSON) document. It consists of a JSON document structured as an array of operation objects, each specifying an action that, when applied sequentially, transforms the original document into a desired resulting state.[1]
The format defines six operations—add, remove, replace, move, copy, and test—that enable precise modifications to JSON structures such as objects, arrays, and values.[1]
The primary purpose of JSON Patch is to facilitate partial updates to JSON documents in RESTful web services, particularly via the HTTP PATCH method, by introducing the media type application/json-patch+json. This approach avoids the transmission of complete documents, reducing bandwidth usage and improving performance for scenarios involving large or frequently updated data.[1] It was developed to address the absence of a JSON-specific standard for HTTP PATCH requests, as outlined in RFC 5789, which defined the method but left payload formats open to interpretation.[1][3]
Key benefits of JSON Patch include its efficiency in handling modifications to extensive JSON documents without requiring full replacements, and its support for atomic application, where the entire patch either succeeds completely or results in no changes to the target document, thereby maintaining data integrity.[1] As a JSON-based format, it also aligns seamlessly with standard JSON processing libraries and tools.[1]
Historical Development
JSON Patch emerged in the early 2010s to address the growing need for efficient partial updates in RESTful web APIs, where full document replacement was inefficient for large JSON payloads. Initial development began with informal discussions and prototypes around 2010–2012, driven by the limitations of HTTP methods like PUT for incremental changes in JSON-based services. The first individual draft, authored by Paul C. Bryan, was submitted to the IETF as draft-pbryan-json-patch-00 in April 2011, introducing a sequence-based operation model for modifying JSON documents.[4] Contributors such as Mike Acar provided feedback during early iterations, refining the specification for broader applicability.[5]
The standardization process advanced through the IETF Applications Area Working Group (APPSAWG), evolving into draft-ietf-appsawg-json-patch. It was published as RFC 6902 in April 2013, with Paul Bryan as editor and Mark Nottingham as co-author, defining the core format for expressing operations like add, remove, and replace.[6] This specification built directly on JSON Pointer (RFC 6901, also published in 2013) for precise addressing within JSON structures, enabling path-based targeting without relying on external schemas.[1] It was motivated by the earlier XML Patch (RFC 5261, 2008), adapting its operation-centric approach to JSON's unordered object semantics, which lack XML's sequential guarantees.[7]
Post-publication, RFC 6902 received clarifications through IETF errata, including Errata ID 4787 (reported 2016) addressing path restrictions for the remove operation to prevent root-level deletions.[8] No major revisions have occurred by 2025, maintaining the original specification as the authoritative standard. Adoption expanded into ecosystem tools, such as integrations for schema updates in JSON Schema implementations and support in OpenAPI 3.1 (released 2021), which allows describing PATCH endpoints using the application/json-patch+json media type for API documentation and validation.
Specification
A JSON Patch document consists of a JSON array containing a sequence of operation objects, each designed to modify a target JSON document when applied in order.[1] Each operation object must include an "op" member specifying the operation type (such as "add", "remove", or others defined in the specification) and a "path" member providing a JSON Pointer string (per RFC 6901) that locates the target location within the document.[1] Additionally, operation objects may include optional members: a "value" member for operations that set or test a value, and a "from" member for "move" and "copy" operations to indicate the source path.[1]
The media type for JSON Patch documents is "application/json-patch+json", which supports the transmission of multiple operations via protocols like HTTP PATCH without required or optional parameters.[1] These operations are applied sequentially to the target JSON document in the order they appear in the array, with each subsequent operation acting on the potentially modified result of the previous one; no parallel execution is implied or supported.[1]
If a path in an operation does not resolve to a valid location in the target document, or if an operation fails due to other requirements (such as attempting a "remove" on a non-existent value), the evaluation of the patch stops, and the entire patch is considered unsuccessful, and the target document remains unmodified.[1] Implementations handling JSON Patch via HTTP PATCH may respond with a 422 (Unprocessable Entity) status code for validation errors, treating the operation as atomic per RFC 5789.[1][3]
For illustration, a basic JSON Patch document might appear as follows, where each array element specifies an operation via its "op" field:
json
[
{
"op": "add",
"path": "/name",
"value": "New Value"
},
{
"op": "remove",
"path": "/unwanted"
}
]
[
{
"op": "add",
"path": "/name",
"value": "New Value"
},
{
"op": "remove",
"path": "/unwanted"
}
]
This structure ensures precise, incremental modifications without transmitting the entire updated document.[1]
Path and Value Handling
JSON Patch employs JSON Pointers, as defined in RFC 6901, to specify locations within a JSON document targeted by operations.[1] A JSON Pointer is a Unicode string composed of zero or more reference tokens, each delimited by a forward slash ('/'), forming a path that resolves step-by-step from the document root.[9] For instance, in a document {"users": [{"name": "Alice"}]} , the pointer /users/0/name targets the string "Alice" by first selecting the "users" object member, then the array index 0, and finally the "name" property.[9] This mechanism supports navigation to object properties via exact member names and to array elements via zero-based integer indices expressed as decimal digits without leading zeros; non-integer tokens are treated as object keys.[9]
When used in URI fragments, a JSON Pointer follows a '#' character, such as #/users/0/name, enabling direct referencing in web contexts while adhering to percent-encoding for URI safety.[9] The root path, represented by a single '/' or an empty string after '#', targets the entire JSON document.[9] Invalid paths, such as those containing non-numeric tokens where an array index is expected or pointing to nonexistent elements (unless creation is permitted by the operation), result in patch application failure, halting evaluation.[1] JSON Pointers do not support regular expressions, wildcards, or partial matching, ensuring precise, deterministic targeting.[9]
Values in JSON Patch operations are handled through the "value" member, which carries a complete JSON value for actions like add and replace, preserving the original JSON types such as strings (quoted), numbers, booleans, arrays, objects, or the explicit null literal.[1] For example, to add a string value, the patch includes "value": "newName", ensuring the type is maintained without coercion.[1] Null values are represented directly as JSON null, integrated seamlessly into the document structure.[1]
To prevent delimiter conflicts in reference tokens, JSON Pointers require escaping: the tilde ('~') is encoded as '~0' and the slash ('/') as '~1' within keys or names.[9] Resolution decodes these by first replacing '1' with '/' and then '0' with '', allowing safe inclusion of literal '/' or '' in object keys, as in the pointer /~11 targeting a key named "/1".[9] This escaping applies only to the tokens themselves, not to the overall path string.[9]
Operations
Add Operation
The "add" operation in JSON Patch is used to insert a value into a specified location within a JSON document, potentially extending the structure if the exact target does not exist. Its syntax consists of a JSON object with the required members "op": "add", "path": a JSON Pointer value indicating the target location, and "value": the JSON value to be added; unlike the "move" or "copy" operations, it does not include an optional "from" member.[10] When applied, the operation either inserts the value at the target if it is absent or replaces the existing value if the target already exists, provided the parent object or array is reachable; the operation fails with an error if the parent does not exist.[10]
For object members, the "add" operation adds a new property with the given value if the member name at the target path does not exist, or replaces the existing value if it does. For example, applying {"op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ]} to the document {"a": {"b": {}}} results in {"a": {"b": {"c": [ "foo", "bar" ]}}}.[10] In array contexts, the operation inserts the value at the specified index, shifting any subsequent elements one position to the right; the index must not exceed the current length of the array, and specifying an index equal to the length (or using the "-" token as per JSON Pointer semantics) appends the value to the end. For instance, applying {"op": "add", "path": "/foo/1", "value": "qux"} to {"foo": ["bar"]} yields {"foo": ["bar", "qux"]}, effectively appending since the original length is 1.[10][11]
If the path targets the root of the document ("/"), the "add" operation replaces the entire document with the provided value, effectively overwriting all existing content.[10] This behavior aligns with the JSON Pointer syntax for paths, where references must resolve to a valid location or the parent thereof for successful insertion.[10] Common use cases include creating new elements in objects or arrays to extend data structures incrementally, such as adding user details to a profile object or appending items to a list, without requiring a full document replacement.[10]
Remove Operation
The remove operation in JSON Patch is used to delete a value at a specified target location within the JSON document. Its syntax consists of a JSON object with the mandatory members "op" set to the string "remove" and "path" specifying the JSON Pointer (as defined in RFC 6901) to the target location; unlike other operations, it does not require a "value" member or a "from" member.[12]
The operation succeeds only if the target location exists in the document prior to application, removing the value there and leaving the document otherwise unchanged except for any necessary adjustments, such as in arrays. If the path resolves to a member of an object, that key-value pair is deleted entirely. For arrays, the element at the specified index is removed, and all subsequent elements are shifted one position to the left to compact the array, with the array length decreased accordingly. The path must resolve to a removable location, such as an object member or array element, per the general rules for path and value handling in the specification.[12][13]
If the target path is the root ("/"), the behavior is not defined in the specification, and implementations may handle it differently (e.g., setting the document to null, an empty object, or failing with an error). Failure occurs if the target location does not exist, resulting in an error (commonly analogous to a 404 status in HTTP contexts), which halts the application of the patch sequence.[12][13]
Regarding idempotency, the standard requires the target to exist for success, meaning a subsequent application of the same remove operation on an already-deleted path will fail with an error; however, some implementations treat missing targets as no-ops to enable safer retries.[12]
Replace Operation
The replace operation in JSON Patch is used to update the value at a specified location in the target JSON document by substituting it with a new value, without altering the document's structure. This operation is defined in the JSON Patch specification as an object containing the members "op": "replace", "path", and "value", where "path" is a JSON Pointer value identifying the target location and "value" specifies the replacement JSON value.[14]
For the replace operation to succeed, the target location referenced by the path must already exist in the document; if it does not, the entire patch application fails. The operation fully overwrites the existing value at that location, preserving the type and structure of the new value provided, and it requires the inclusion of a "value" member in the operation object. Unlike operations that modify arrays by shifting elements, replace does not affect the positions of other elements.[14]
A key distinction from the add operation is that replace strictly requires the path to point to an existing value and cannot create new members or array elements, whereas add would insert a value if the path is absent. This ensures replace is used only for in-place updates, preventing unintended structural changes. For instance, attempting to replace at a non-existent array index would fail, requiring an add operation instead to append or insert.[14]
In practice, the replace operation is commonly employed in RESTful APIs for partial resource updates, such as modifying user profile fields like email or status in a database-backed service, allowing efficient changes without transmitting the entire document. It can also follow a test operation to validate the current value before replacement, ensuring safe updates in concurrent environments.[15]
Move Operation
The move operation in JSON Patch relocates an existing value within a JSON document by removing it from a source location and inserting it at a target location. This operation is defined in RFC 6902 and requires an operation object with three members: "op" set to "move", "from" specifying the JSON Pointer to the source path, and "path" specifying the JSON Pointer to the target path; notably, it does not include a "value" member, as the value is derived from the source.[16]
The behavior of the move operation combines the semantics of the remove operation (deleting the value at the "from" path) followed immediately by the add operation (inserting that value at the "path" target, adhering to add rules such as creating intermediate objects or appending to arrays if necessary). For the operation to succeed, the "from" path must reference an existing value in the document prior to application; if it does not, the operation fails. The target "path" follows standard add behavior: if the parent exists and is an object, the value is added as a new member (effectively renaming object keys if moving within the same object); if the parent is an array, the value is inserted at the specified index, shifting subsequent elements. In arrays, moving an element to a new index adjusts the indices of intervening elements accordingly, preserving the overall structure without data loss or duplication.[16]
A key limitation is that the operation fails if the "from" path is a proper prefix of the "path" (i.e., it cannot move a value to a descendant location of itself, preventing infinite loops or structural cycles). Additionally, the target path must be valid under add semantics; for instance, moving to a non-existent parent object without intermediate creation support would fail, though JSON Patch generally allows parent creation during add. These rules ensure atomicity and prevent invalid states during patching.[16]
Common use cases for the move operation include reordering elements in arrays, such as shifting items in a list without recreating the data, or restructuring nested objects by relocating substructures to new positions, thereby maintaining data integrity while avoiding redundant transmission of values. For example, to move the value at /a/b/c to /a/b/d in an object, the patch document would be:
[
{"op": "move", "from": "/a/b/c", "path": "/a/b/d"}
]
[
{"op": "move", "from": "/a/b/c", "path": "/a/b/d"}
]
This relocates the value without altering its content, contrasting with operations that insert new data. Similarly, moving an array element from index 1 to index 3 in /items would shift indices 2 and 3 accordingly:
[
{"op": "move", "from": "/items/1", "path": "/items/3"}
]
[
{"op": "move", "from": "/items/1", "path": "/items/3"}
]
Such applications are particularly useful in scenarios like updating user interfaces or migrating data models efficiently.[17][18]
Copy Operation
The copy operation in JSON Patch allows for duplicating a value from one location within the target JSON document to another location, without removing or altering the source value.[1] The operation is specified as a JSON object with the required members "op": "copy", "from", and "path", where "from" is a JSON Pointer identifying the source location and "path" is a JSON Pointer specifying the target location; unlike the add operation, no "value" member is included since the value is derived from the source.[1]
This operation behaves equivalently to an add operation at the target location, but using the value retrieved from the source path, meaning the insertion or replacement at the target follows the same rules as add (e.g., creating new object members or appending to arrays if the target does not exist).[1] The source location specified by "from" must exist in the document at the time the operation is evaluated; if it does not, the operation fails and patch application terminates.[1] Similarly, the operation will fail if the target location cannot be created or modified according to add rules, such as attempting to add to a non-object or non-array parent.[1] For example, the patch [{"op": "copy", "from": "/a/b/c", "path": "/a/b/e"}] copies the value at /a/b/c to /a/b/e, leaving the original value intact.[1]
The copy operation is commonly used for duplicating substructures or elements within a document, such as replicating default configuration settings or adding an item to a list while preserving the original.[19] This makes it suitable for scenarios like templating repeated sections in configuration files or extending arrays without manual value specification.[19]
Test Operation
The "test" operation in JSON Patch is designed to validate whether the value at a specified path in the target JSON document matches an expected value, enabling conditional logic without modifying the document. This operation is particularly useful for ensuring that a patch sequence only proceeds if certain preconditions are met, such as verifying the current state before applying subsequent changes. If the test succeeds, the patch application continues; otherwise, it fails and halts, preventing unintended modifications.[20]
The syntax for the test operation requires three members in the operation object: "op" set to "test", "path" specifying the JSON Pointer to the target location, and "value" providing the expected JSON value for comparison. For instance, the following operation tests whether the value at /a/b/c equals the string "foo":
json
{"op": "test", "path": "/a/b/c", "value": "foo"}
{"op": "test", "path": "/a/b/c", "value": "foo"}
The path must resolve to an existing value in the document; if it does not, the operation fails. The comparison uses deep equality, meaning the structures and types must match exactly, including for complex types like arrays and objects.[20]
Equality is determined by the following rules, which align with JSON's type system: strings are compared byte-by-byte for identical Unicode characters; numbers must be numerically equal; booleans and null literals must be identical; arrays require the same length with corresponding elements equal by these rules; and objects require the same set of member names with equal keys and values. This structural matching ensures precise validation, such as confirming array order or object key presence. For example, testing an array path against [1, 2] would fail if the document holds [2, 1], even though the elements are the same. These rules support safe conditional patching, often preceding operations like replace to verify preconditions.[20]
In practice, the test operation facilitates robust patch application in scenarios requiring state verification, such as checking a version field before an update to avoid conflicts. An example from the specification applies a test to confirm that /baz holds "qux" in the document {"baz": "qux"}, succeeding and allowing further operations; conversely, testing against "bar" in the same document fails with an error, as the values differ. This mechanism is essential for maintaining data integrity in sequential or distributed patching workflows.[21][22]
Examples
Simple Document Modification
To illustrate the application of JSON Patch in a basic scenario, consider an initial JSON document representing a simple user profile:
json
{
"name": "John",
"age": 30
}
{
"name": "John",
"age": 30
}
A JSON Patch document consists of an array of operations, each specifying an "op" type, a "path" using JSON Pointer notation, and relevant values.[1] For a single modification, apply an "add" operation to insert a new member at the root level.[1] The path "/city" resolves to the document's root object, where the new key-value pair is appended if the key does not exist.[1]
The patch document is:
json
[
{
"op": "add",
"path": "/city",
"value": "New York"
}
]
[
{
"op": "add",
"path": "/city",
"value": "New York"
}
]
Applying this patch to the initial document results in:
json
{
"name": "John",
"age": 30,
"city": "New York"
}
{
"name": "John",
"age": 30,
"city": "New York"
}
This process demonstrates how JSON Patch enables precise, atomic updates with minimal overhead for small-scale document modifications, as the operation targets only the specified location without altering unrelated parts of the structure.[1]
Multi-Operation Sequence
In JSON Patch, multiple operations are applied sequentially from left to right to a target document, with each subsequent operation acting on the modified result of the previous one.[23] This sequential evaluation allows for complex transformations that build upon intermediate states, such as conditional checks followed by structural changes. If any operation fails—such as a "test" operation verifying a mismatched value—the entire patch application terminates without any modifications to the document, ensuring atomicity.[24]
Consider an initial document representing a user array:
json
{
"users": [
{
"id": 1,
"name": "Alice"
}
]
}
{
"users": [
{
"id": 1,
"name": "Alice"
}
]
}
A multi-operation patch might first test the existence and value of the first user's name to confirm the document state, then add a new user, and finally replace the first user's ID. The patch document would be:
json
[
{
"op": "test",
"path": "/users/0/name",
"value": "Alice"
},
{
"op": "add",
"path": "/users/1",
"value": {
"id": 2,
"name": "Bob"
}
},
{
"op": "replace",
"path": "/users/0/id",
"value": 3
}
]
[
{
"op": "test",
"path": "/users/0/name",
"value": "Alice"
},
{
"op": "add",
"path": "/users/1",
"value": {
"id": 2,
"name": "Bob"
}
},
{
"op": "replace",
"path": "/users/0/id",
"value": 3
}
]
Applying this sequence: The "test" operation succeeds, leaving the document unchanged. The "add" operation then appends the new user object at index 1, resulting in an intermediate document with two users (IDs 1 and 2). Finally, the "replace" updates the first user's ID to 3, yielding the final document:
json
{
"users": [
{
"id": 3,
"name": "Alice"
},
{
"id": 2,
"name": "Bob"
}
]
}
{
"users": [
{
"id": 3,
"name": "Alice"
},
{
"id": 2,
"name": "Bob"
}
]
}
If the initial "test" had used an incorrect value (e.g., "Bob"), the patch would fail entirely, reverting to the original document with no changes applied.[20] This all-or-nothing behavior aligns with the atomic semantics recommended for HTTP PATCH requests, preventing inconsistent partial updates in distributed systems.[24] Implementations may return an error code (e.g., HTTP 422) upon failure, but the target document remains unmodified.[24]
Comparison to JSON Merge Patch
JSON Merge Patch, defined in RFC 7386, provides a declarative approach to modifying JSON documents by submitting a single JSON object that represents the desired final state or partial changes.[2] In this format, members present in the patch object overwrite corresponding values in the target document, absent members remain unchanged, and null values explicitly remove members from the target.[2] This recursive merging process simplifies updates for object-based structures but assumes the patch conveys the complete intended structure for modified sections, potentially leading to lossy results if the original document contains explicit nulls or non-object elements like arrays.[2]
In contrast, JSON Patch, as specified in RFC 6902, employs an operation-based format consisting of an array of discrete actions (such as add, remove, replace, move, copy, and test) applied sequentially to the target document.[1] This explicit, imperative model allows for precise, reversible modifications that preserve order sensitivity, enabling complex scenarios like moving elements between locations or testing conditions before applying changes—capabilities absent in Merge Patch.[1] While Merge Patch's declarative nature can overwrite entire substructures efficiently without specifying paths for every change, it struggles with fine-grained control, such as deleting specific array elements or expressing moves, often requiring a full replacement of the affected array.[2]
For example, to update a user's address by moving the city to a new field and removing an obsolete entry, JSON Patch might use an array like [{"op": "move", "from": "/address/city", "path": "/newField"}, {"op": "remove", "path": "/obsolete"}], ensuring atomic, targeted execution.[1] Merge Patch, however, would require providing the entire updated address object with nulls for deletions, potentially disrupting unrelated fields if not carefully constructed, and it cannot natively handle the move operation.[2]
JSON Patch suits scenarios demanding granular control and auditability, such as RESTful APIs for partial resource updates where reversibility and conditional tests enhance reliability.[1] Conversely, Merge Patch excels in simpler bulk overwrites or when patching nested objects without needing to track individual operations, though it may introduce inefficiencies for sparse changes.[2] The two formats are not mutually exclusive and can complement each other in systems supporting multiple PATCH strategies under HTTP.[3]
Integration with JSON Pointer
JSON Patch fundamentally depends on JSON Pointer (defined in RFC 6901) for specifying the locations of operations within a target JSON document. Each operation in a JSON Patch document includes a "path" member whose value is a string representing a JSON Pointer, which identifies a precise location such as an object member, array element, or nested value. This integration allows JSON Patch to leverage the standardized syntax of JSON Pointer for targeting without introducing a proprietary addressing mechanism, ensuring interoperability and precision in modifications.[13]
While JSON Pointer itself is designed primarily for querying and referencing locations within JSON documents, JSON Patch extends this capability by defining a set of mutating operations—such as add, remove, replace, move, copy, and test—that act upon those referenced locations. For instance, the "from" member in move and copy operations also uses a JSON Pointer to specify source locations. This extension transforms JSON Pointer from a static addressing tool into a dynamic framework for document transformation, enabling atomic and reversible changes to JSON structures.[16][25]
In RESTful APIs, JSON Pointer and JSON Patch support efficient partial updates via HTTP PATCH requests without full document retransmissions. Subsequent patches can use JSON Pointers to target sub-elements of fetched documents.[1]
One limitation arises from JSON Pointer's support for URI fragment identifiers, which allow direct addressing of document parts via constructs like #/path/to/value in media types that register JSON Pointer semantics. JSON Patch does not natively incorporate this fragment mode; instead, paths are relative to the entire target document identified by the request URI, requiring full URIs in API contexts for resource-level operations and potentially additional server-side handling for fragment-based partial updates.[26]