How to apply a JSON Patch in JavaScript, Python, Go and Java

A JSON Patch is the standard, reviewable way to move a JSON document from A to B: a list of operations with JSON Pointer paths, not a pile of copy-pasted lines. This page takes one example patch and applies it in five languages with complete runnable code; the JSON Compare tool generates the patch for you from any two documents.

The example this page applies

The document:

{
  "name": "red sneaker",
  "sizes": [40, 41, 42],
  "in_stock": true,
  "tags": ["summer", "sale"]
}

The patch, one op per line, covering all six RFC 6902 operations:

[
  { "op": "test", "path": "/in_stock", "value": true },
  { "op": "add", "path": "/price", "value": 59.0 },
  { "op": "replace", "path": "/name", "value": "red sneaker, wide fit" },
  { "op": "copy", "from": "/name", "path": "/display_name" },
  { "op": "move", "from": "/tags/1", "path": "/tags/0" },
  { "op": "remove", "path": "/sizes/2" }
]

Applying it produces the same result in JavaScript and Python (verified on fast-json-patch 3.1.1 and jsonpatch 1.35; JavaScript prints 59, Python 59.0, same number):

{
  "name": "red sneaker, wide fit",
  "sizes": [40, 41],
  "in_stock": true,
  "tags": ["sale", "summer"],
  "price": 59,
  "display_name": "red sneaker, wide fit"
}

Apply a JSON Patch in JavaScript

Install: npm install fast-json-patch

// apply-patch.mjs, then: node apply-patch.mjs
import pkg from "fast-json-patch";
const { applyPatch } = pkg;

const doc = {
  "name": "red sneaker",
  "sizes": [40, 41, 42],
  "in_stock": true,
  "tags": ["summer", "sale"]
};

const patch = [
  { "op": "test", "path": "/in_stock", "value": true },
  { "op": "add", "path": "/price", "value": 59.0 },
  { "op": "replace", "path": "/name", "value": "red sneaker, wide fit" },
  { "op": "copy", "from": "/name", "path": "/display_name" },
  { "op": "move", "from": "/tags/1", "path": "/tags/0" },
  { "op": "remove", "path": "/sizes/2" }
];

const results = applyPatch(doc, patch);
const patched = results[results.length - 1].newDocument;
console.log(JSON.stringify(patched, null, 2));

fast-json-patch ships CommonJS, so plain Node runs the default-import form; TypeScript and bundlers accept the named import too. The patch mutates doc in place, and each result carries newDocument as of that op, which is why the last one is the output above.

Apply a JSON Patch in Python

Install: pip install jsonpatch

# apply_patch.py, then: python apply_patch.py
import json
import jsonpatch

doc = {
    "name": "red sneaker",
    "sizes": [40, 41, 42],
    "in_stock": True,
    "tags": ["summer", "sale"]
}

patch = jsonpatch.JsonPatch([
    {"op": "test", "path": "/in_stock", "value": True},
    {"op": "add", "path": "/price", "value": 59.0},
    {"op": "replace", "path": "/name", "value": "red sneaker, wide fit"},
    {"op": "copy", "from": "/name", "path": "/display_name"},
    {"op": "move", "from": "/tags/1", "path": "/tags/0"},
    {"op": "remove", "path": "/sizes/2"}
])

result = patch.apply(doc)
print(json.dumps(result, indent=2))

The patch is written as Python literals here: True, not true. If your patch arrives as raw JSON text, parse it with json.loads first and pass the parsed list.

Apply a JSON Patch in Go

Install: go get github.com/evanphx/json-patch/v5

// main.go, then: go run main.go
package main

import (
	"fmt"

	jsonpatch "github.com/evanphx/json-patch/v5"
)

func main() {
	doc := []byte(`{
  "name": "red sneaker",
  "sizes": [40, 41, 42],
  "in_stock": true,
  "tags": ["summer", "sale"]
}`)
	patch := []byte(`[
  { "op": "test", "path": "/in_stock", "value": true },
  { "op": "add", "path": "/price", "value": 59.0 },
  { "op": "replace", "path": "/name", "value": "red sneaker, wide fit" },
  { "op": "copy", "from": "/name", "path": "/display_name" },
  { "op": "move", "from": "/tags/1", "path": "/tags/0" },
  { "op": "remove", "path": "/sizes/2" }
]`)

	p, err := jsonpatch.DecodePatch(patch)
	if err != nil {
		panic(err)
	}

	out, err := p.Apply(doc)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(out))
}

Both inputs are raw JSON bytes and Apply hands back patched bytes; the library re-marshals through Go maps, so the printed keys come out sorted and compact, with the data unchanged.

Apply a JSON Patch in Java

Install: Maven io.github.vishwakarma:zjsonpatch:0.6.3 plus com.fasterxml.jackson.core:jackson-databind:2.18.2 (the package is the flipkart-incubator/zjsonpatch library; Jackson became an optional dependency in 0.6.0, so add it yourself).

// src/ApplyPatch.java, Java 17+
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flipkart.zjsonpatch.JsonPatch;

public class ApplyPatch {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        JsonNode doc = mapper.readTree("""
            {
              "name": "red sneaker",
              "sizes": [40, 41, 42],
              "in_stock": true,
              "tags": ["summer", "sale"]
            }""");
        JsonNode patch = mapper.readTree("""
            [
              { "op": "test", "path": "/in_stock", "value": true },
              { "op": "add", "path": "/price", "value": 59.0 },
              { "op": "replace", "path": "/name", "value": "red sneaker, wide fit" },
              { "op": "copy", "from": "/name", "path": "/display_name" },
              { "op": "move", "from": "/tags/1", "path": "/tags/0" },
              { "op": "remove", "path": "/sizes/2" }
            ]""");

        JsonNode patched = JsonPatch.apply(patch, doc);
        System.out.println(patched.toPrettyString());
    }
}

The argument order is (patch, source), and apply works on a clone, so doc comes out untouched.

Apply a JSON Patch in C#

Install: dotnet add package JsonPatch.Net

// Program.cs, .NET 7+ (raw string literals)
using System.Text.Json;
using System.Text.Json.Nodes;
using Json.Patch;

var patch = JsonSerializer.Deserialize<JsonPatch>("""
    [
      { "op": "test", "path": "/in_stock", "value": true },
      { "op": "add", "path": "/price", "value": 59.0 },
      { "op": "replace", "path": "/name", "value": "red sneaker, wide fit" },
      { "op": "copy", "from": "/name", "path": "/display_name" },
      { "op": "move", "from": "/tags/1", "path": "/tags/0" },
      { "op": "remove", "path": "/sizes/2" }
    ]""");
var doc = JsonNode.Parse("""
    {
      "name": "red sneaker",
      "sizes": [40, 41, 42],
      "in_stock": true,
      "tags": ["summer", "sale"]
    }""");

var result = patch.Apply(doc);
Console.WriteLine(result.Result);

Apply returns a PatchResult: .Result holds the patched document, .Error explains a failed patch, and .IsSuccess tells you which one you got.

The six RFC 6902 operations

Full spec: RFC 6902. Library catalog for other languages: jsonpatch.com.

Frequently Asked Questions

What is a JSON Patch?

A JSON Patch is an array of operations defined by RFC 6902 that transforms one JSON document into another: add, remove, replace, move, copy and test, each pointing at a JSON Pointer path like user.tags[2]. It is the standard way to hand a machine a set of changes, whether that is a patch library, an HTTP PATCH endpoint or a CI script.

How do I apply a JSON Patch in Python?

With the jsonpatch library: pip install jsonpatch, wrap the operations in jsonpatch.JsonPatch([...]), and call .apply(doc), which returns the patched document. Inside the patch list the values are Python values, so booleans are True and None stays None; to keep the patch as raw JSON text, parse it with json.loads first.

Why does my JSON Patch fail to apply?

The three usual suspects: a replace or remove path that does not exist in the document, an add whose parent is missing, and a test operation whose value does not match. Patches apply as a sequence and stop at the first failing operation, so the ops before it stay applied only if you work on a copy.