JSON Patch: Apply and Generate RFC 6902 and Merge Patches

Paste a document and a patch to see the result, or paste two versions of a document to get the patch between them. Everything runs in your browser; failing patches change nothing and show which operation broke.

Patch type
Task

Document

JSON Patch

Result

What a JSON Patch is

A JSON Patch is a JSON array of operations applied in order. It is the body format of HTTP PATCH requests sent with Content-Type: application/json-patch+json, and kubectl patch --type=json accepts it too. Each operation is an object with an op and a path:

  • add inserts value at path. On an object it sets the member, replacing any existing one; on an array it inserts before the given index.
  • remove deletes the value at path, which must exist.
  • replace swaps the existing value at path for value.
  • move takes the value at from and adds it at path. A value cannot be moved into one of its own children.
  • copy adds a deep copy of the value at from at path.
  • test checks that the value at path equals value. Numbers compare by value and object members in any order.

Members the standard does not define, such as a comment field, are ignored.

Paths are JSON Pointers

Paths use JSON Pointer (RFC 6901): /limits/cpu is the cpu member of limits, and /features/0 is the first item of features. The empty string "" means the whole document, so {"op": "replace", "path": "", "value": {}} swaps everything.

Two characters need escaping inside a key: write ~0 for a tilde and ~1 for a slash, so the key a/b is /a~1b. The final token - means the slot after the last array item, and is how you append: {"op": "add", "path": "/features/-", "value": "x"}. Array indexes are plain whole numbers; 01 or 1e0 are rejected rather than guessed at.

All or nothing, with a precise error

The standard requires a patch to apply completely or not at all, and this tool works the same way: the document is copied, every operation runs on the copy, and the first failure discards it. The status line names the operation by its index in the array, its op and path, says what went wrong, and the Patch editor marks the exact line, for example:

Operation [1] test "/replicas": test failed: the value is 2, expected 3

That makes test a safe guard for optimistic updates: put {"op": "test", "path": "/version", "value": "1.4.2"} first, and the rest only applies to the version you expect.

Merge Patch: the simpler alternative

A JSON Merge Patch is a partial document instead of an operation list: members it contains are set, members set to null are deleted, nested objects merge recursively, and anything else (including arrays) replaces the old value outright. It is the application/merge-patch+json format and kubectl patch --type=merge.

The trade-offs are inherent to the format. A merge patch cannot set a member to null, because null means delete, and it cannot insert one item into an array; it must resend the whole array. When a generated merge patch hits the first case, the tool lists the affected paths so you can switch to JSON Patch.

Generating a patch from two documents

Choose Generate from two documents, paste the original and the changed version, and the tool writes the patch. Arrays are aligned with a diff algorithm, so inserting an item produces one add at the right index instead of replacing every later item; an item that moved becomes a remove and an add. Objects are compared member by member, and key order is ignored because JSON objects are unordered.

Press Apply this patch to load the generated patch into the Patch editor and check that the result equals the changed document. Big integers keep all their digits in both the patch and the result. To see the differences as a readable list first, use JSON Diff, which exports the same patches.

Examples

Update a service config with every operation

A test guards the version, then replace, append with “/-”, remove, move (a rename) and copy. Changing the test value to “1.4.1” makes the whole patch fail and leaves the document untouched.

Document
{
  "name": "checkout-service",
  "version": "1.4.2",
  "replicas": 2,
  "features": ["cart", "coupons"],
  "limits": { "cpu": "500m", "memory": "256Mi" },
  "debug": true
}
JSON Patch
[
  { "op": "test", "path": "/version", "value": "1.4.2" },
  { "op": "replace", "path": "/version", "value": "1.5.0" },
  { "op": "add", "path": "/features/-", "value": "gift-cards" },
  { "op": "remove", "path": "/debug" },
  { "op": "move", "from": "/limits/memory", "path": "/limits/mem" },
  { "op": "copy", "from": "/replicas", "path": "/minReplicas" }
]
Result
{
  "name": "checkout-service",
  "version": "1.5.0",
  "replicas": 2,
  "features": [
    "cart",
    "coupons",
    "gift-cards"
  ],
  "limits": {
    "cpu": "500m",
    "mem": "256Mi"
  },
  "minReplicas": 2
}
Load this example into the tool

Merge patch: set, delete and replace an array

The title is set, null deletes author.email while author.name survives, the tags array is replaced whole, and members the patch does not mention stay as they were.

Document
{
  "title": "Weekly report",
  "author": { "name": "Grace", "email": "grace@example.com" },
  "tags": ["draft", "finance"],
  "reviewers": ["sam"]
}
Merge Patch
{
  "title": "Weekly report (final)",
  "author": { "email": null },
  "tags": ["finance"],
  "published": true
}
Result
{
  "title": "Weekly report (final)",
  "author": {
    "name": "Grace"
  },
  "tags": [
    "finance"
  ],
  "reviewers": [
    "sam"
  ],
  "published": true
}
Load this example into the tool

Generate a patch from two product records

An inserted color becomes one add at index 1 rather than a rewrite of the array; the price change, the new flag and the dropped stock field follow.

Original
{"sku": "A-100", "price": 19.99, "stock": 12, "colors": ["red", "blue"]}
Changed
{"sku": "A-100", "price": 17.99, "colors": ["red", "green", "blue"], "sale": true}
Result
[
  {
    "op": "add",
    "path": "/colors/1",
    "value": "green"
  },
  {
    "op": "replace",
    "path": "/price",
    "value": 17.99
  },
  {
    "op": "add",
    "path": "/sale",
    "value": true
  },
  {
    "op": "remove",
    "path": "/stock"
  }
]
Load this example into the tool

Common errors and how to fix them

ErrorCauseFix
Operation [2] remove "/tags/3": index 3 in "/tags/3" is out of range: the array has 2 itemsThe index does not exist, often because an earlier operation in the same patch already removed an item and shifted the rest.Operations run in order, so count indexes after the previous operations, or remove items from the highest index down.
Operation [0] add "/settings/theme": "/settings" does not existadd creates only the last step of the path; every parent must already exist.Add the parent first, e.g. {“op”: “add”, “path”: “/settings”, “value”: {“theme”: “dark”}}.
A JSON Patch must be an array of operations, not an objectThe patch is a single operation object, or it is really a merge patch.Wrap the operation in [ ], or switch the patch type to Merge Patch.
Operation [1] test "/replicas": test failed: the value is 2, expected 3The document is not in the state the patch expects, so the whole patch is rejected by design.Fetch the current document and regenerate the patch, or correct the expected value in the test.
"/a~b" has an invalid escape: "~" must be followed by 0 (for ~) or 1 (for /)A key containing ~ or / was written into the path without escaping.Write ~ as ~0 and / as ~1 inside a key: the key “a~b” is the path /a~0b.

Frequently asked questions

Should I use JSON Patch or JSON Merge Patch?

Use Merge Patch for simple partial updates of objects; it reads like the data itself. Use JSON Patch when you edit arrays item by item, need to set values to null, or want test operations that guard against concurrent changes.

If one operation fails, are the earlier ones kept?

No. RFC 6902 makes a patch atomic, and this tool follows it: any failure leaves the document exactly as it was, and the error names the failing operation and its line.

How do I append to an array?

Use add with “-” as the last path token, for example “/items/-”. Using the array length as the index also works; any larger index is an error.

Can the document or patch be YAML?

Yes. Both editors accept JSON, JSON5 and YAML, which is handy for Kubernetes patches written in YAML. The result is always printed as JSON.

Is the patch sent to a server?

No. Parsing, applying and generating all happen in a worker inside this browser tab, and nothing you paste is uploaded or stored.

Why does a generated merge patch warn about null?

In a merge patch, null means “delete this member”, so it cannot express a member whose new value is null. The warning lists those paths; the JSON Patch for the same change is exact.

Related tools