1# JSON-Patch
2`jsonpatch` is a library which provides functionality for both applying
3[RFC6902 JSON patches](http://tools.ietf.org/html/rfc6902) against documents, as
4well as for calculating & applying [RFC7396 JSON merge patches](https://tools.ietf.org/html/rfc7396).
5
6[](http://godoc.org/github.com/evanphx/json-patch)
7[](https://travis-ci.org/evanphx/json-patch)
8[](https://goreportcard.com/report/github.com/evanphx/json-patch)
9
10# Get It!
11
12**Latest and greatest**:
13```bash
14go get -u github.com/evanphx/json-patch/v5
15```
16
17**Stable Versions**:
18* Version 5: `go get -u gopkg.in/evanphx/json-patch.v5`
19* Version 4: `go get -u gopkg.in/evanphx/json-patch.v4`
20
21(previous versions below `v3` are unavailable)
22
23# Use It!
24* [Create and apply a merge patch](#create-and-apply-a-merge-patch)
25* [Create and apply a JSON Patch](#create-and-apply-a-json-patch)
26* [Comparing JSON documents](#comparing-json-documents)
27* [Combine merge patches](#combine-merge-patches)
28
29
30# Configuration
31
32* There is a global configuration variable `jsonpatch.SupportNegativeIndices`.
33 This defaults to `true` and enables the non-standard practice of allowing
34 negative indices to mean indices starting at the end of an array. This
35 functionality can be disabled by setting `jsonpatch.SupportNegativeIndices =
36 false`.
37
38* There is a global configuration variable `jsonpatch.AccumulatedCopySizeLimit`,
39 which limits the total size increase in bytes caused by "copy" operations in a
40 patch. It defaults to 0, which means there is no limit.
41
42These global variables control the behavior of `jsonpatch.Apply`.
43
44An alternative to `jsonpatch.Apply` is `jsonpatch.ApplyWithOptions` whose behavior
45is controlled by an `options` parameter of type `*jsonpatch.ApplyOptions`.
46
47Structure `jsonpatch.ApplyOptions` includes the configuration options above
48and adds two new options: `AllowMissingPathOnRemove` and `EnsurePathExistsOnAdd`.
49
50When `AllowMissingPathOnRemove` is set to `true`, `jsonpatch.ApplyWithOptions` will ignore
51`remove` operations whose `path` points to a non-existent location in the JSON document.
52`AllowMissingPathOnRemove` defaults to `false` which will lead to `jsonpatch.ApplyWithOptions`
53returning an error when hitting a missing `path` on `remove`.
54
55When `EnsurePathExistsOnAdd` is set to `true`, `jsonpatch.ApplyWithOptions` will make sure
56that `add` operations produce all the `path` elements that are missing from the target object.
57
58Use `jsonpatch.NewApplyOptions` to create an instance of `jsonpatch.ApplyOptions`
59whose values are populated from the global configuration variables.
60
61## Create and apply a merge patch
62Given both an original JSON document and a modified JSON document, you can create
63a [Merge Patch](https://tools.ietf.org/html/rfc7396) document.
64
65It can describe the changes needed to convert from the original to the
66modified JSON document.
67
68Once you have a merge patch, you can apply it to other JSON documents using the
69`jsonpatch.MergePatch(document, patch)` function.
70
71```go
72package main
73
74import (
75 "fmt"
76
77 jsonpatch "github.com/evanphx/json-patch"
78)
79
80func main() {
81 // Let's create a merge patch from these two documents...
82 original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
83 target := []byte(`{"name": "Jane", "age": 24}`)
84
85 patch, err := jsonpatch.CreateMergePatch(original, target)
86 if err != nil {
87 panic(err)
88 }
89
90 // Now lets apply the patch against a different JSON document...
91
92 alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`)
93 modifiedAlternative, err := jsonpatch.MergePatch(alternative, patch)
94
95 fmt.Printf("patch document: %s\n", patch)
96 fmt.Printf("updated alternative doc: %s\n", modifiedAlternative)
97}
98```
99
100When ran, you get the following output:
101
102```bash
103$ go run main.go
104patch document: {"height":null,"name":"Jane"}
105updated alternative doc: {"age":28,"name":"Jane"}
106```
107
108## Create and apply a JSON Patch
109You can create patch objects using `DecodePatch([]byte)`, which can then
110be applied against JSON documents.
111
112The following is an example of creating a patch from two operations, and
113applying it against a JSON document.
114
115```go
116package main
117
118import (
119 "fmt"
120
121 jsonpatch "github.com/evanphx/json-patch"
122)
123
124func main() {
125 original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
126 patchJSON := []byte(`[
127 {"op": "replace", "path": "/name", "value": "Jane"},
128 {"op": "remove", "path": "/height"}
129 ]`)
130
131 patch, err := jsonpatch.DecodePatch(patchJSON)
132 if err != nil {
133 panic(err)
134 }
135
136 modified, err := patch.Apply(original)
137 if err != nil {
138 panic(err)
139 }
140
141 fmt.Printf("Original document: %s\n", original)
142 fmt.Printf("Modified document: %s\n", modified)
143}
144```
145
146When ran, you get the following output:
147
148```bash
149$ go run main.go
150Original document: {"name": "John", "age": 24, "height": 3.21}
151Modified document: {"age":24,"name":"Jane"}
152```
153
154## Comparing JSON documents
155Due to potential whitespace and ordering differences, one cannot simply compare
156JSON strings or byte-arrays directly.
157
158As such, you can instead use `jsonpatch.Equal(document1, document2)` to
159determine if two JSON documents are _structurally_ equal. This ignores
160whitespace differences, and key-value ordering.
161
162```go
163package main
164
165import (
166 "fmt"
167
168 jsonpatch "github.com/evanphx/json-patch"
169)
170
171func main() {
172 original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
173 similar := []byte(`
174 {
175 "age": 24,
176 "height": 3.21,
177 "name": "John"
178 }
179 `)
180 different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`)
181
182 if jsonpatch.Equal(original, similar) {
183 fmt.Println(`"original" is structurally equal to "similar"`)
184 }
185
186 if !jsonpatch.Equal(original, different) {
187 fmt.Println(`"original" is _not_ structurally equal to "different"`)
188 }
189}
190```
191
192When ran, you get the following output:
193```bash
194$ go run main.go
195"original" is structurally equal to "similar"
196"original" is _not_ structurally equal to "different"
197```
198
199## Combine merge patches
200Given two JSON merge patch documents, it is possible to combine them into a
201single merge patch which can describe both set of changes.
202
203The resulting merge patch can be used such that applying it results in a
204document structurally similar as merging each merge patch to the document
205in succession.
206
207```go
208package main
209
210import (
211 "fmt"
212
213 jsonpatch "github.com/evanphx/json-patch"
214)
215
216func main() {
217 original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
218
219 nameAndHeight := []byte(`{"height":null,"name":"Jane"}`)
220 ageAndEyes := []byte(`{"age":4.23,"eyes":"blue"}`)
221
222 // Let's combine these merge patch documents...
223 combinedPatch, err := jsonpatch.MergeMergePatches(nameAndHeight, ageAndEyes)
224 if err != nil {
225 panic(err)
226 }
227
228 // Apply each patch individual against the original document
229 withoutCombinedPatch, err := jsonpatch.MergePatch(original, nameAndHeight)
230 if err != nil {
231 panic(err)
232 }
233
234 withoutCombinedPatch, err = jsonpatch.MergePatch(withoutCombinedPatch, ageAndEyes)
235 if err != nil {
236 panic(err)
237 }
238
239 // Apply the combined patch against the original document
240
241 withCombinedPatch, err := jsonpatch.MergePatch(original, combinedPatch)
242 if err != nil {
243 panic(err)
244 }
245
246 // Do both result in the same thing? They should!
247 if jsonpatch.Equal(withCombinedPatch, withoutCombinedPatch) {
248 fmt.Println("Both JSON documents are structurally the same!")
249 }
250
251 fmt.Printf("combined merge patch: %s", combinedPatch)
252}
253```
254
255When ran, you get the following output:
256```bash
257$ go run main.go
258Both JSON documents are structurally the same!
259combined merge patch: {"age":4.23,"eyes":"blue","height":null,"name":"Jane"}
260```
261
262# CLI for comparing JSON documents
263You can install the commandline program `json-patch`.
264
265This program can take multiple JSON patch documents as arguments,
266and fed a JSON document from `stdin`. It will apply the patch(es) against
267the document and output the modified doc.
268
269**patch.1.json**
270```json
271[
272 {"op": "replace", "path": "/name", "value": "Jane"},
273 {"op": "remove", "path": "/height"}
274]
275```
276
277**patch.2.json**
278```json
279[
280 {"op": "add", "path": "/address", "value": "123 Main St"},
281 {"op": "replace", "path": "/age", "value": "21"}
282]
283```
284
285**document.json**
286```json
287{
288 "name": "John",
289 "age": 24,
290 "height": 3.21
291}
292```
293
294You can then run:
295
296```bash
297$ go install github.com/evanphx/json-patch/cmd/json-patch
298$ cat document.json | json-patch -p patch.1.json -p patch.2.json
299{"address":"123 Main St","age":"21","name":"Jane"}
300```
301
302# Help It!
303Contributions are welcomed! Leave [an issue](https://github.com/evanphx/json-patch/issues)
304or [create a PR](https://github.com/evanphx/json-patch/compare).
305
306
307Before creating a pull request, we'd ask that you make sure tests are passing
308and that you have added new tests when applicable.
309
310Contributors can run tests using:
311
312```bash
313go test -cover ./...
314```
315
316Builds for pull requests are tested automatically
317using [TravisCI](https://travis-ci.org/evanphx/json-patch).
View as plain text