...

Text file src/github.com/evanphx/json-patch/README.md

Documentation: github.com/evanphx/json-patch

     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[![GoDoc](https://godoc.org/github.com/evanphx/json-patch?status.svg)](http://godoc.org/github.com/evanphx/json-patch)
     7[![Build Status](https://github.com/evanphx/json-patch/actions/workflows/go.yml/badge.svg)](https://github.com/evanphx/json-patch/actions/workflows/go.yml)
     8[![Report Card](https://goreportcard.com/badge/github.com/evanphx/json-patch)](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 [GitHub Actions](https://github.com/evanphx/json-patch/actions/workflows/go.yml).

View as plain text