Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
JSON Patch Operations for Configuration Updates
Updating configuration stored as JSON can sometimes be cumbersome. Sending the entire new configuration, even for small changes, can be inefficient, especially over slow networks or when configurations are large. It can also lead to race conditions if multiple updates happen concurrently.JSON Patch (RFC 6902)provides a standard method for applying partial updates to a JSON document, making configuration changes more efficient and robust.
What is JSON Patch?
JSON Patch is a format for describing changes to a JSON document. It's defined as an array of operation objects. Each operation object specifies a single change to be made to the target JSON document.
Instead of sending the complete new version of a configuration, you send a small array of operations that describe exactly how to transform the old configuration into the new one.
Why Use JSON Patch for Configurations?
- Efficiency: Sends only the changes, not the entire document, saving bandwidth and processing time.
- Atomicity: A patch document represents a single set of changes. Applying the patch is often done transactionally.
- Reduced Race Conditions: When applying a patch server-side, you can often use a "test" operation (see below) to verify the current state before making changes, mitigating race conditions.
- Auditability: A sequence of patches provides a clear history of how a configuration evolved.
- Standardization: It's an IETF standard, meaning libraries exist for many programming languages.
JSON Pointer (RFC 6901) for Paths
JSON Patch operations use JSON Pointer (RFC 6901) to identify the specific part of the JSON document being operated on. A JSON Pointer is a string starting with /
that navigates through the JSON structure.
/foo/bar
: Points to the value of the "bar" key inside the "foo" object./foo/0
: Points to the first element (index 0) of the array that is the value of the "foo" key./
: Points to the root of the document./a~1b/c~0d
: JSON Pointer requires escaping~
as~0
and/
as~1
.- To add to an array, use
/-
as the last segment to append to the end.
Example JSON Pointer Paths:
{ "application": { "name": "My App", "version": "1.0", "settings": { "theme": "dark", "timeout": 30000 }, "features": [ "featureA", "featureB" ], "special/key~name": "value" } } Path /application points to: { "name": "My App", ... } Path /application/name points to: "My App" Path /application/settings/theme points to: "dark" Path /application/features/0 points to: "featureA" Path /application/features/1 points to: "featureB" Path /application/features/- points to: (the position *after* the last element) Path /application/special~1key~0name points to: "value" Path / points to the whole document.
JSON Patch Operations
There are six standard operations. Each operation object must have an "op"
key specifying the type of operation.
1. add
The "add" operation inserts a new value into an object or array.
Structure: { "op": "add", "path": "/...", "value": "..." }
- If the target is an object, it adds a new key-value pair. If the key exists, it behaves like "replace".
- If the target is an array, it inserts the value at the specified index (shifting subsequent elements). Using
/-
appends the value to the end.
Add Example:
Initial Configuration:
{ "logging": { "level": "info" }, "features": ["analytics"] }
Patch: Add a "format" key to logging and a new feature:
[ { "op": "add", "path": "/logging/format", "value": "json" }, { "op": "add", "path": "/features/-", "value": "notifications" } ]
Resulting Configuration:
{ "logging": { "level": "info", "format": "json" }, "features": ["analytics", "notifications"] }
2. remove
The "remove" operation removes a value from an object or array.
Structure: { "op": "remove", "path": "/..." }
- If the target is an object, it removes the key-value pair.
- If the target is an array, it removes the element at the specified index (shifting subsequent elements).
Remove Example:
Initial Configuration:
{ "database": { "host": "localhost", "port": 5432 }, "features": ["analytics", "notifications"] }
Patch: Remove the "port" and the "analytics" feature:
[ { "op": "remove", "path": "/database/port" }, { "op": "remove", "path": "/features/0" } // Remove first element ]
Resulting Configuration:
{ "database": { "host": "localhost" }, "features": ["notifications"] // Index 1 shifted to 0 }
3. replace
The "replace" operation replaces a value at a specific path. This is the most common operation for simple property updates.
Structure: { "op": "replace", "path": "/...", "value": "..." }
- The path must point to an existing value.
- If the path points to a non-existent location, the operation fails.
Replace Example:
Initial Configuration:
{ "application": { "version": "1.0" }, "settings": { "timeout": 30000 } }
Patch: Update the version and timeout:
[ { "op": "replace", "path": "/application/version", "value": "1.1" }, { "op": "replace", "path": "/settings/timeout", "value": 60000 } ]
Resulting Configuration:
{ "application": { "version": "1.1" }, "settings": { "timeout": 60000 } }
4. move
The "move" operation moves a value from one location in the document to another.
Structure: { "op": "move", "from": "/...", "path": "/..." }
- Both
from
andpath
are JSON Pointers. - The value is removed from the
from
location and added at thepath
location. - This is useful for restructuring the configuration.
Move Example:
Initial Configuration:
{ "old_settings": { "theme": "dark", "legacy": true }, "new_settings": {} }
Patch: Move "theme" from "old_settings" to "new_settings":
[ { "op": "move", "from": "/old_settings/theme", "path": "/new_settings/theme" } ]
Resulting Configuration:
{ "old_settings": { "legacy": true }, "new_settings": { "theme": "dark" } }
5. copy
The "copy" operation copies a value from one location to another. It's similar to "move" but the original value remains.
Structure: { "op": "copy", "from": "/...", "path": "/..." }
- Both
from
andpath
are JSON Pointers. - A duplicate of the value at
from
is created at thepath
location.
Copy Example:
Initial Configuration:
{ "default_settings": { "timeout": 30000, "retries": 3 }, "user_settings": {} }
Patch: Copy the default timeout to user settings:
[ { "op": "copy", "from": "/default_settings/timeout", "path": "/user_settings/timeout" } ]
Resulting Configuration:
{ "default_settings": { "timeout": 30000, "retries": 3 }, "user_settings": { "timeout": 30000 } }
6. test
The "test" operation verifies that the value at a specified location is equal to a given value.
Structure: { "op": "test", "path": "/...", "value": "..." }
- If the value at
path
is not exactly equal to the providedvalue
, the entire patch application should fail. - This is crucial for preventing race conditions. You can test that a configuration value is what you expect it to be before applying changes based on that assumption.
Test Example (Success Scenario):
Initial Configuration:
{ "status": "active", "count": 5 }
Patch: Test status and update count (assuming status is "active"):
[ { "op": "test", "path": "/status", "value": "active" }, { "op": "replace", "path": "/count", "value": 6 } ]
Result: Patch succeeds, resulting in:
{ "status": "active", "count": 6 }
Test Example (Failure Scenario):
Initial Configuration:
{ "status": "inactive", "count": 5 }
Patch: Test status and update count (same patch as above):
[ { "op": "test", "path": "/status", "value": "active" }, { "op": "replace", "path": "/count", "value": 6 } ]
Result: The "test" operation fails because "status"
is "inactive"
, not "active"
. The entire patch is rejected, and the configuration remains unchanged.
{ "status": "inactive", "count": 5 }
Implementing JSON Patch
You don't typically write the patch application logic yourself. Libraries that implement RFC 6902 and RFC 6901 are available in most languages.
To use JSON Patch for configuration updates:
- Retrieve the current configuration (JSON document).
- Determine the difference between the current configuration and the desired new configuration. This step often requires a JSON Diff algorithm or library that can generate a patch from two documents.
- Send the generated JSON Patch (array of operations) to the service responsible for updating the configuration.
- The service uses a JSON Patch library to apply the patch to its current version of the configuration. It should perform all operations atomically. If any operation (especially "test") fails, the entire patch application should be rolled back or aborted.
Beyond Configuration Updates
JSON Patch is useful in many scenarios beyond just configuration:
- REST APIs: Supporting
PATCH
requests where clients send a JSON Patch document to partially update a resource. - Real-time Collaboration: Sharing patches between clients to synchronize document states (similar to how diff/patch works in version control).
- Database Updates: Using JSON Patch to describe changes to JSON fields in databases that support the JSON type.
Conclusion
JSON Patch provides a standardized, efficient, and robust way to apply granular updates to JSON documents. For managing configuration updates, it offers significant advantages in terms of bandwidth, clarity, and safety (especially with the "test" operation). By leveraging existing libraries, developers can easily incorporate JSON Patch into their systems for more sophisticated configuration management and data synchronization.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool