├── .github ├── dependabot.yaml └── workflows │ ├── cifuzz.yml │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── cmd └── json-patch │ ├── file_flag.go │ └── main.go ├── errors.go ├── merge.go ├── merge_test.go ├── patch.go ├── patch_test.go └── v5 ├── bench_test.go ├── cmd └── json-patch │ ├── file_flag.go │ └── main.go ├── errors.go ├── go.mod ├── go.sum ├── internal └── json │ ├── decode.go │ ├── decode_test.go │ ├── encode.go │ ├── encode_test.go │ ├── example_marshaling_test.go │ ├── example_test.go │ ├── example_text_marshaling_test.go │ ├── fold.go │ ├── fold_test.go │ ├── fuzz.go │ ├── fuzz_test.go │ ├── indent.go │ ├── number_test.go │ ├── scanner.go │ ├── scanner_test.go │ ├── stream.go │ ├── stream_test.go │ ├── tables.go │ ├── tagkey_test.go │ ├── tags.go │ ├── tags_test.go │ └── testdata │ └── code.json.gz ├── merge.go ├── merge_test.go ├── patch.go └── patch_test.go /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/v5" 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" -------------------------------------------------------------------------------- /.github/workflows/cifuzz.yml: -------------------------------------------------------------------------------- 1 | name: CIFuzz 2 | on: [pull_request] 3 | jobs: 4 | Fuzzing: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Build Fuzzers 8 | id: build 9 | uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master 10 | with: 11 | oss-fuzz-project-name: 'json-patch' 12 | dry-run: false 13 | language: go 14 | - name: Run Fuzzers 15 | uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master 16 | with: 17 | oss-fuzz-project-name: 'json-patch' 18 | fuzz-seconds: 300 19 | dry-run: false 20 | language: go 21 | - name: Upload Crash 22 | uses: actions/upload-artifact@v4 23 | if: failure() && steps.build.outcome == 'success' 24 | with: 25 | name: artifacts 26 | path: ./out/artifacts 27 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go Modules 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up Go 15 | uses: actions/setup-go@v5 16 | with: 17 | go-version: 1.18 18 | 19 | - name: Test 20 | run: | 21 | cd v5 22 | go get ./... 23 | go test -v ./... 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # editor and IDE paraphernalia 2 | .idea 3 | .vscode 4 | 5 | # macOS paraphernalia 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Evan Phoenix 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of the Evan Phoenix nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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 4 | well 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 14 | go get -u github.com/evanphx/json-patch/v5 15 | ``` 16 | 17 | If you need version 4, use `go get -u gopkg.in/evanphx/json-patch.v4` 18 | 19 | (previous versions below `v3` are unavailable) 20 | 21 | # Use It! 22 | * [Create and apply a merge patch](#create-and-apply-a-merge-patch) 23 | * [Create and apply a JSON Patch](#create-and-apply-a-json-patch) 24 | * [Comparing JSON documents](#comparing-json-documents) 25 | * [Combine merge patches](#combine-merge-patches) 26 | 27 | 28 | # Configuration 29 | 30 | * There is a global configuration variable `jsonpatch.SupportNegativeIndices`. 31 | This defaults to `true` and enables the non-standard practice of allowing 32 | negative indices to mean indices starting at the end of an array. This 33 | functionality can be disabled by setting `jsonpatch.SupportNegativeIndices = 34 | false`. 35 | 36 | * There is a global configuration variable `jsonpatch.AccumulatedCopySizeLimit`, 37 | which limits the total size increase in bytes caused by "copy" operations in a 38 | patch. It defaults to 0, which means there is no limit. 39 | 40 | These global variables control the behavior of `jsonpatch.Apply`. 41 | 42 | An alternative to `jsonpatch.Apply` is `jsonpatch.ApplyWithOptions` whose behavior 43 | is controlled by an `options` parameter of type `*jsonpatch.ApplyOptions`. 44 | 45 | Structure `jsonpatch.ApplyOptions` includes the configuration options above 46 | and adds two new options: `AllowMissingPathOnRemove` and `EnsurePathExistsOnAdd`. 47 | 48 | When `AllowMissingPathOnRemove` is set to `true`, `jsonpatch.ApplyWithOptions` will ignore 49 | `remove` operations whose `path` points to a non-existent location in the JSON document. 50 | `AllowMissingPathOnRemove` defaults to `false` which will lead to `jsonpatch.ApplyWithOptions` 51 | returning an error when hitting a missing `path` on `remove`. 52 | 53 | When `EnsurePathExistsOnAdd` is set to `true`, `jsonpatch.ApplyWithOptions` will make sure 54 | that `add` operations produce all the `path` elements that are missing from the target object. 55 | 56 | Use `jsonpatch.NewApplyOptions` to create an instance of `jsonpatch.ApplyOptions` 57 | whose values are populated from the global configuration variables. 58 | 59 | ## Create and apply a merge patch 60 | Given both an original JSON document and a modified JSON document, you can create 61 | a [Merge Patch](https://tools.ietf.org/html/rfc7396) document. 62 | 63 | It can describe the changes needed to convert from the original to the 64 | modified JSON document. 65 | 66 | Once you have a merge patch, you can apply it to other JSON documents using the 67 | `jsonpatch.MergePatch(document, patch)` function. 68 | 69 | ```go 70 | package main 71 | 72 | import ( 73 | "fmt" 74 | 75 | jsonpatch "github.com/evanphx/json-patch" 76 | ) 77 | 78 | func main() { 79 | // Let's create a merge patch from these two documents... 80 | original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) 81 | target := []byte(`{"name": "Jane", "age": 24}`) 82 | 83 | patch, err := jsonpatch.CreateMergePatch(original, target) 84 | if err != nil { 85 | panic(err) 86 | } 87 | 88 | // Now lets apply the patch against a different JSON document... 89 | 90 | alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`) 91 | modifiedAlternative, err := jsonpatch.MergePatch(alternative, patch) 92 | 93 | fmt.Printf("patch document: %s\n", patch) 94 | fmt.Printf("updated alternative doc: %s\n", modifiedAlternative) 95 | } 96 | ``` 97 | 98 | When ran, you get the following output: 99 | 100 | ```bash 101 | $ go run main.go 102 | patch document: {"height":null,"name":"Jane"} 103 | updated alternative doc: {"age":28,"name":"Jane"} 104 | ``` 105 | 106 | ## Create and apply a JSON Patch 107 | You can create patch objects using `DecodePatch([]byte)`, which can then 108 | be applied against JSON documents. 109 | 110 | The following is an example of creating a patch from two operations, and 111 | applying it against a JSON document. 112 | 113 | ```go 114 | package main 115 | 116 | import ( 117 | "fmt" 118 | 119 | jsonpatch "github.com/evanphx/json-patch" 120 | ) 121 | 122 | func main() { 123 | original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) 124 | patchJSON := []byte(`[ 125 | {"op": "replace", "path": "/name", "value": "Jane"}, 126 | {"op": "remove", "path": "/height"} 127 | ]`) 128 | 129 | patch, err := jsonpatch.DecodePatch(patchJSON) 130 | if err != nil { 131 | panic(err) 132 | } 133 | 134 | modified, err := patch.Apply(original) 135 | if err != nil { 136 | panic(err) 137 | } 138 | 139 | fmt.Printf("Original document: %s\n", original) 140 | fmt.Printf("Modified document: %s\n", modified) 141 | } 142 | ``` 143 | 144 | When ran, you get the following output: 145 | 146 | ```bash 147 | $ go run main.go 148 | Original document: {"name": "John", "age": 24, "height": 3.21} 149 | Modified document: {"age":24,"name":"Jane"} 150 | ``` 151 | 152 | ## Comparing JSON documents 153 | Due to potential whitespace and ordering differences, one cannot simply compare 154 | JSON strings or byte-arrays directly. 155 | 156 | As such, you can instead use `jsonpatch.Equal(document1, document2)` to 157 | determine if two JSON documents are _structurally_ equal. This ignores 158 | whitespace differences, and key-value ordering. 159 | 160 | ```go 161 | package main 162 | 163 | import ( 164 | "fmt" 165 | 166 | jsonpatch "github.com/evanphx/json-patch" 167 | ) 168 | 169 | func main() { 170 | original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) 171 | similar := []byte(` 172 | { 173 | "age": 24, 174 | "height": 3.21, 175 | "name": "John" 176 | } 177 | `) 178 | different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`) 179 | 180 | if jsonpatch.Equal(original, similar) { 181 | fmt.Println(`"original" is structurally equal to "similar"`) 182 | } 183 | 184 | if !jsonpatch.Equal(original, different) { 185 | fmt.Println(`"original" is _not_ structurally equal to "different"`) 186 | } 187 | } 188 | ``` 189 | 190 | When ran, you get the following output: 191 | ```bash 192 | $ go run main.go 193 | "original" is structurally equal to "similar" 194 | "original" is _not_ structurally equal to "different" 195 | ``` 196 | 197 | ## Combine merge patches 198 | Given two JSON merge patch documents, it is possible to combine them into a 199 | single merge patch which can describe both set of changes. 200 | 201 | The resulting merge patch can be used such that applying it results in a 202 | document structurally similar as merging each merge patch to the document 203 | in succession. 204 | 205 | ```go 206 | package main 207 | 208 | import ( 209 | "fmt" 210 | 211 | jsonpatch "github.com/evanphx/json-patch" 212 | ) 213 | 214 | func main() { 215 | original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) 216 | 217 | nameAndHeight := []byte(`{"height":null,"name":"Jane"}`) 218 | ageAndEyes := []byte(`{"age":4.23,"eyes":"blue"}`) 219 | 220 | // Let's combine these merge patch documents... 221 | combinedPatch, err := jsonpatch.MergeMergePatches(nameAndHeight, ageAndEyes) 222 | if err != nil { 223 | panic(err) 224 | } 225 | 226 | // Apply each patch individual against the original document 227 | withoutCombinedPatch, err := jsonpatch.MergePatch(original, nameAndHeight) 228 | if err != nil { 229 | panic(err) 230 | } 231 | 232 | withoutCombinedPatch, err = jsonpatch.MergePatch(withoutCombinedPatch, ageAndEyes) 233 | if err != nil { 234 | panic(err) 235 | } 236 | 237 | // Apply the combined patch against the original document 238 | 239 | withCombinedPatch, err := jsonpatch.MergePatch(original, combinedPatch) 240 | if err != nil { 241 | panic(err) 242 | } 243 | 244 | // Do both result in the same thing? They should! 245 | if jsonpatch.Equal(withCombinedPatch, withoutCombinedPatch) { 246 | fmt.Println("Both JSON documents are structurally the same!") 247 | } 248 | 249 | fmt.Printf("combined merge patch: %s", combinedPatch) 250 | } 251 | ``` 252 | 253 | When ran, you get the following output: 254 | ```bash 255 | $ go run main.go 256 | Both JSON documents are structurally the same! 257 | combined merge patch: {"age":4.23,"eyes":"blue","height":null,"name":"Jane"} 258 | ``` 259 | 260 | # CLI for comparing JSON documents 261 | You can install the commandline program `json-patch`. 262 | 263 | This program can take multiple JSON patch documents as arguments, 264 | and fed a JSON document from `stdin`. It will apply the patch(es) against 265 | the document and output the modified doc. 266 | 267 | **patch.1.json** 268 | ```json 269 | [ 270 | {"op": "replace", "path": "/name", "value": "Jane"}, 271 | {"op": "remove", "path": "/height"} 272 | ] 273 | ``` 274 | 275 | **patch.2.json** 276 | ```json 277 | [ 278 | {"op": "add", "path": "/address", "value": "123 Main St"}, 279 | {"op": "replace", "path": "/age", "value": "21"} 280 | ] 281 | ``` 282 | 283 | **document.json** 284 | ```json 285 | { 286 | "name": "John", 287 | "age": 24, 288 | "height": 3.21 289 | } 290 | ``` 291 | 292 | You can then run: 293 | 294 | ```bash 295 | $ go install github.com/evanphx/json-patch/cmd/json-patch 296 | $ cat document.json | json-patch -p patch.1.json -p patch.2.json 297 | {"address":"123 Main St","age":"21","name":"Jane"} 298 | ``` 299 | 300 | # Help It! 301 | Contributions are welcomed! Leave [an issue](https://github.com/evanphx/json-patch/issues) 302 | or [create a PR](https://github.com/evanphx/json-patch/compare). 303 | 304 | 305 | Before creating a pull request, we'd ask that you make sure tests are passing 306 | and that you have added new tests when applicable. 307 | 308 | Contributors can run tests using: 309 | 310 | ```bash 311 | go test -cover ./... 312 | ``` 313 | 314 | Builds for pull requests are tested automatically 315 | using [GitHub Actions](https://github.com/evanphx/json-patch/actions/workflows/go.yml). 316 | -------------------------------------------------------------------------------- /cmd/json-patch/file_flag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Borrowed from Concourse: https://github.com/concourse/atc/blob/master/atccmd/file_flag.go 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | // FileFlag is a flag for passing a path to a file on disk. The file is 12 | // expected to be a file, not a directory, that actually exists. 13 | type FileFlag string 14 | 15 | // UnmarshalFlag implements go-flag's Unmarshaler interface 16 | func (f *FileFlag) UnmarshalFlag(value string) error { 17 | stat, err := os.Stat(value) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | if stat.IsDir() { 23 | return fmt.Errorf("path '%s' is a directory, not a file", value) 24 | } 25 | 26 | abs, err := filepath.Abs(value) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | *f = FileFlag(abs) 32 | 33 | return nil 34 | } 35 | 36 | // Path is the path to the file 37 | func (f FileFlag) Path() string { 38 | return string(f) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/json-patch/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | 9 | jsonpatch "github.com/evanphx/json-patch" 10 | flags "github.com/jessevdk/go-flags" 11 | ) 12 | 13 | type opts struct { 14 | PatchFilePaths []FileFlag `long:"patch-file" short:"p" value-name:"PATH" description:"Path to file with one or more operations"` 15 | } 16 | 17 | func main() { 18 | var o opts 19 | _, err := flags.Parse(&o) 20 | if err != nil { 21 | log.Fatalf("error: %s\n", err) 22 | } 23 | 24 | patches := make([]jsonpatch.Patch, len(o.PatchFilePaths)) 25 | 26 | for i, patchFilePath := range o.PatchFilePaths { 27 | var bs []byte 28 | bs, err = ioutil.ReadFile(patchFilePath.Path()) 29 | if err != nil { 30 | log.Fatalf("error reading patch file: %s", err) 31 | } 32 | 33 | var patch jsonpatch.Patch 34 | patch, err = jsonpatch.DecodePatch(bs) 35 | if err != nil { 36 | log.Fatalf("error decoding patch file: %s", err) 37 | } 38 | 39 | patches[i] = patch 40 | } 41 | 42 | doc, err := ioutil.ReadAll(os.Stdin) 43 | if err != nil { 44 | log.Fatalf("error reading from stdin: %s", err) 45 | } 46 | 47 | mdoc := doc 48 | for _, patch := range patches { 49 | mdoc, err = patch.Apply(mdoc) 50 | if err != nil { 51 | log.Fatalf("error applying patch: %s", err) 52 | } 53 | } 54 | 55 | fmt.Printf("%s", mdoc) 56 | } 57 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package jsonpatch 2 | 3 | import "fmt" 4 | 5 | // AccumulatedCopySizeError is an error type returned when the accumulated size 6 | // increase caused by copy operations in a patch operation has exceeded the 7 | // limit. 8 | type AccumulatedCopySizeError struct { 9 | limit int64 10 | accumulated int64 11 | } 12 | 13 | // NewAccumulatedCopySizeError returns an AccumulatedCopySizeError. 14 | func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError { 15 | return &AccumulatedCopySizeError{limit: l, accumulated: a} 16 | } 17 | 18 | // Error implements the error interface. 19 | func (a *AccumulatedCopySizeError) Error() string { 20 | return fmt.Sprintf("Unable to complete the copy, the accumulated size increase of copy is %d, exceeding the limit %d", a.accumulated, a.limit) 21 | } 22 | 23 | // ArraySizeError is an error type returned when the array size has exceeded 24 | // the limit. 25 | type ArraySizeError struct { 26 | limit int 27 | size int 28 | } 29 | 30 | // NewArraySizeError returns an ArraySizeError. 31 | func NewArraySizeError(l, s int) *ArraySizeError { 32 | return &ArraySizeError{limit: l, size: s} 33 | } 34 | 35 | // Error implements the error interface. 36 | func (a *ArraySizeError) Error() string { 37 | return fmt.Sprintf("Unable to create array of size %d, limit is %d", a.size, a.limit) 38 | } 39 | -------------------------------------------------------------------------------- /merge.go: -------------------------------------------------------------------------------- 1 | package jsonpatch 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | ) 9 | 10 | func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode { 11 | curDoc, err := cur.intoDoc() 12 | 13 | if err != nil { 14 | pruneNulls(patch) 15 | return patch 16 | } 17 | 18 | patchDoc, err := patch.intoDoc() 19 | 20 | if err != nil { 21 | return patch 22 | } 23 | 24 | mergeDocs(curDoc, patchDoc, mergeMerge) 25 | 26 | return cur 27 | } 28 | 29 | func mergeDocs(doc, patch *partialDoc, mergeMerge bool) { 30 | for k, v := range *patch { 31 | if v == nil { 32 | if mergeMerge { 33 | (*doc)[k] = nil 34 | } else { 35 | delete(*doc, k) 36 | } 37 | } else { 38 | cur, ok := (*doc)[k] 39 | 40 | if !ok || cur == nil { 41 | if !mergeMerge { 42 | pruneNulls(v) 43 | } 44 | 45 | (*doc)[k] = v 46 | } else { 47 | (*doc)[k] = merge(cur, v, mergeMerge) 48 | } 49 | } 50 | } 51 | } 52 | 53 | func pruneNulls(n *lazyNode) { 54 | sub, err := n.intoDoc() 55 | 56 | if err == nil { 57 | pruneDocNulls(sub) 58 | } else { 59 | ary, err := n.intoAry() 60 | 61 | if err == nil { 62 | pruneAryNulls(ary) 63 | } 64 | } 65 | } 66 | 67 | func pruneDocNulls(doc *partialDoc) *partialDoc { 68 | for k, v := range *doc { 69 | if v == nil { 70 | delete(*doc, k) 71 | } else { 72 | pruneNulls(v) 73 | } 74 | } 75 | 76 | return doc 77 | } 78 | 79 | func pruneAryNulls(ary *partialArray) *partialArray { 80 | newAry := []*lazyNode{} 81 | 82 | for _, v := range *ary { 83 | if v != nil { 84 | pruneNulls(v) 85 | } 86 | newAry = append(newAry, v) 87 | } 88 | 89 | *ary = newAry 90 | 91 | return ary 92 | } 93 | 94 | var ErrBadJSONDoc = fmt.Errorf("Invalid JSON Document") 95 | var ErrBadJSONPatch = fmt.Errorf("Invalid JSON Patch") 96 | var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents") 97 | 98 | // MergeMergePatches merges two merge patches together, such that 99 | // applying this resulting merged merge patch to a document yields the same 100 | // as merging each merge patch to the document in succession. 101 | func MergeMergePatches(patch1Data, patch2Data []byte) ([]byte, error) { 102 | return doMergePatch(patch1Data, patch2Data, true) 103 | } 104 | 105 | // MergePatch merges the patchData into the docData. 106 | func MergePatch(docData, patchData []byte) ([]byte, error) { 107 | return doMergePatch(docData, patchData, false) 108 | } 109 | 110 | func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { 111 | doc := &partialDoc{} 112 | 113 | docErr := json.Unmarshal(docData, doc) 114 | 115 | patch := &partialDoc{} 116 | 117 | patchErr := json.Unmarshal(patchData, patch) 118 | 119 | if _, ok := docErr.(*json.SyntaxError); ok { 120 | return nil, ErrBadJSONDoc 121 | } 122 | 123 | if _, ok := patchErr.(*json.SyntaxError); ok { 124 | return nil, ErrBadJSONPatch 125 | } 126 | 127 | if docErr == nil && *doc == nil { 128 | return nil, ErrBadJSONDoc 129 | } 130 | 131 | if patchErr == nil && *patch == nil { 132 | return nil, ErrBadJSONPatch 133 | } 134 | 135 | if docErr != nil || patchErr != nil { 136 | // Not an error, just not a doc, so we turn straight into the patch 137 | if patchErr == nil { 138 | if mergeMerge { 139 | doc = patch 140 | } else { 141 | doc = pruneDocNulls(patch) 142 | } 143 | } else { 144 | patchAry := &partialArray{} 145 | patchErr = json.Unmarshal(patchData, patchAry) 146 | 147 | if patchErr != nil { 148 | return nil, ErrBadJSONPatch 149 | } 150 | 151 | pruneAryNulls(patchAry) 152 | 153 | out, patchErr := json.Marshal(patchAry) 154 | 155 | if patchErr != nil { 156 | return nil, ErrBadJSONPatch 157 | } 158 | 159 | return out, nil 160 | } 161 | } else { 162 | mergeDocs(doc, patch, mergeMerge) 163 | } 164 | 165 | return json.Marshal(doc) 166 | } 167 | 168 | // resemblesJSONArray indicates whether the byte-slice "appears" to be 169 | // a JSON array or not. 170 | // False-positives are possible, as this function does not check the internal 171 | // structure of the array. It only checks that the outer syntax is present and 172 | // correct. 173 | func resemblesJSONArray(input []byte) bool { 174 | input = bytes.TrimSpace(input) 175 | 176 | hasPrefix := bytes.HasPrefix(input, []byte("[")) 177 | hasSuffix := bytes.HasSuffix(input, []byte("]")) 178 | 179 | return hasPrefix && hasSuffix 180 | } 181 | 182 | // CreateMergePatch will return a merge patch document capable of converting 183 | // the original document(s) to the modified document(s). 184 | // The parameters can be bytes of either two JSON Documents, or two arrays of 185 | // JSON documents. 186 | // The merge patch returned follows the specification defined at http://tools.ietf.org/html/draft-ietf-appsawg-json-merge-patch-07 187 | func CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { 188 | originalResemblesArray := resemblesJSONArray(originalJSON) 189 | modifiedResemblesArray := resemblesJSONArray(modifiedJSON) 190 | 191 | // Do both byte-slices seem like JSON arrays? 192 | if originalResemblesArray && modifiedResemblesArray { 193 | return createArrayMergePatch(originalJSON, modifiedJSON) 194 | } 195 | 196 | // Are both byte-slices are not arrays? Then they are likely JSON objects... 197 | if !originalResemblesArray && !modifiedResemblesArray { 198 | return createObjectMergePatch(originalJSON, modifiedJSON) 199 | } 200 | 201 | // None of the above? Then return an error because of mismatched types. 202 | return nil, errBadMergeTypes 203 | } 204 | 205 | // createObjectMergePatch will return a merge-patch document capable of 206 | // converting the original document to the modified document. 207 | func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { 208 | originalDoc := map[string]interface{}{} 209 | modifiedDoc := map[string]interface{}{} 210 | 211 | err := json.Unmarshal(originalJSON, &originalDoc) 212 | if err != nil { 213 | return nil, ErrBadJSONDoc 214 | } 215 | 216 | err = json.Unmarshal(modifiedJSON, &modifiedDoc) 217 | if err != nil { 218 | return nil, ErrBadJSONDoc 219 | } 220 | 221 | dest, err := getDiff(originalDoc, modifiedDoc) 222 | if err != nil { 223 | return nil, err 224 | } 225 | 226 | return json.Marshal(dest) 227 | } 228 | 229 | // createArrayMergePatch will return an array of merge-patch documents capable 230 | // of converting the original document to the modified document for each 231 | // pair of JSON documents provided in the arrays. 232 | // Arrays of mismatched sizes will result in an error. 233 | func createArrayMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { 234 | originalDocs := []json.RawMessage{} 235 | modifiedDocs := []json.RawMessage{} 236 | 237 | err := json.Unmarshal(originalJSON, &originalDocs) 238 | if err != nil { 239 | return nil, ErrBadJSONDoc 240 | } 241 | 242 | err = json.Unmarshal(modifiedJSON, &modifiedDocs) 243 | if err != nil { 244 | return nil, ErrBadJSONDoc 245 | } 246 | 247 | total := len(originalDocs) 248 | if len(modifiedDocs) != total { 249 | return nil, ErrBadJSONDoc 250 | } 251 | 252 | result := []json.RawMessage{} 253 | for i := 0; i < len(originalDocs); i++ { 254 | original := originalDocs[i] 255 | modified := modifiedDocs[i] 256 | 257 | patch, err := createObjectMergePatch(original, modified) 258 | if err != nil { 259 | return nil, err 260 | } 261 | 262 | result = append(result, json.RawMessage(patch)) 263 | } 264 | 265 | return json.Marshal(result) 266 | } 267 | 268 | // Returns true if the array matches (must be json types). 269 | // As is idiomatic for go, an empty array is not the same as a nil array. 270 | func matchesArray(a, b []interface{}) bool { 271 | if len(a) != len(b) { 272 | return false 273 | } 274 | if (a == nil && b != nil) || (a != nil && b == nil) { 275 | return false 276 | } 277 | for i := range a { 278 | if !matchesValue(a[i], b[i]) { 279 | return false 280 | } 281 | } 282 | return true 283 | } 284 | 285 | // Returns true if the values matches (must be json types) 286 | // The types of the values must match, otherwise it will always return false 287 | // If two map[string]interface{} are given, all elements must match. 288 | func matchesValue(av, bv interface{}) bool { 289 | if reflect.TypeOf(av) != reflect.TypeOf(bv) { 290 | return false 291 | } 292 | switch at := av.(type) { 293 | case string: 294 | bt := bv.(string) 295 | if bt == at { 296 | return true 297 | } 298 | case float64: 299 | bt := bv.(float64) 300 | if bt == at { 301 | return true 302 | } 303 | case bool: 304 | bt := bv.(bool) 305 | if bt == at { 306 | return true 307 | } 308 | case nil: 309 | // Both nil, fine. 310 | return true 311 | case map[string]interface{}: 312 | bt := bv.(map[string]interface{}) 313 | if len(bt) != len(at) { 314 | return false 315 | } 316 | for key := range bt { 317 | av, aOK := at[key] 318 | bv, bOK := bt[key] 319 | if aOK != bOK { 320 | return false 321 | } 322 | if !matchesValue(av, bv) { 323 | return false 324 | } 325 | } 326 | return true 327 | case []interface{}: 328 | bt := bv.([]interface{}) 329 | return matchesArray(at, bt) 330 | } 331 | return false 332 | } 333 | 334 | // getDiff returns the (recursive) difference between a and b as a map[string]interface{}. 335 | func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) { 336 | into := map[string]interface{}{} 337 | for key, bv := range b { 338 | av, ok := a[key] 339 | // value was added 340 | if !ok { 341 | into[key] = bv 342 | continue 343 | } 344 | // If types have changed, replace completely 345 | if reflect.TypeOf(av) != reflect.TypeOf(bv) { 346 | into[key] = bv 347 | continue 348 | } 349 | // Types are the same, compare values 350 | switch at := av.(type) { 351 | case map[string]interface{}: 352 | bt := bv.(map[string]interface{}) 353 | dst := make(map[string]interface{}, len(bt)) 354 | dst, err := getDiff(at, bt) 355 | if err != nil { 356 | return nil, err 357 | } 358 | if len(dst) > 0 { 359 | into[key] = dst 360 | } 361 | case string, float64, bool: 362 | if !matchesValue(av, bv) { 363 | into[key] = bv 364 | } 365 | case []interface{}: 366 | bt := bv.([]interface{}) 367 | if !matchesArray(at, bt) { 368 | into[key] = bv 369 | } 370 | case nil: 371 | switch bv.(type) { 372 | case nil: 373 | // Both nil, fine. 374 | default: 375 | into[key] = bv 376 | } 377 | default: 378 | panic(fmt.Sprintf("Unknown type:%T in key %s", av, key)) 379 | } 380 | } 381 | // Now add all deleted values as nil 382 | for key := range a { 383 | _, found := b[key] 384 | if !found { 385 | into[key] = nil 386 | } 387 | } 388 | return into, nil 389 | } 390 | -------------------------------------------------------------------------------- /merge_test.go: -------------------------------------------------------------------------------- 1 | package jsonpatch 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func mergePatch(doc, patch string) string { 10 | out, err := MergePatch([]byte(doc), []byte(patch)) 11 | 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | return string(out) 17 | } 18 | 19 | func TestMergePatchReplaceKey(t *testing.T) { 20 | doc := `{ "title": "hello" }` 21 | pat := `{ "title": "goodbye" }` 22 | 23 | res := mergePatch(doc, pat) 24 | 25 | if !compareJSON(pat, res) { 26 | t.Fatalf("Key was not replaced") 27 | } 28 | } 29 | 30 | func TestMergePatchIgnoresOtherValues(t *testing.T) { 31 | doc := `{ "title": "hello", "age": 18 }` 32 | pat := `{ "title": "goodbye" }` 33 | 34 | res := mergePatch(doc, pat) 35 | 36 | exp := `{ "title": "goodbye", "age": 18 }` 37 | 38 | if !compareJSON(exp, res) { 39 | t.Fatalf("Key was not replaced") 40 | } 41 | } 42 | 43 | func TestMergePatchNilDoc(t *testing.T) { 44 | doc := `{ "title": null }` 45 | pat := `{ "title": {"foo": "bar"} }` 46 | 47 | res := mergePatch(doc, pat) 48 | 49 | exp := `{ "title": {"foo": "bar"} }` 50 | 51 | if !compareJSON(exp, res) { 52 | t.Fatalf("Key was not replaced") 53 | } 54 | } 55 | 56 | type arrayCases struct { 57 | original, patch, res string 58 | } 59 | 60 | func TestMergePatchNilArray(t *testing.T) { 61 | 62 | cases := []arrayCases { 63 | {`{"a": [ {"b":"c"} ] }`, `{"a": [1]}`, `{"a": [1]}`}, 64 | {`{"a": [ {"b":"c"} ] }`, `{"a": [null, 1]}`, `{"a": [null, 1]}`}, 65 | {`["a",null]`, `[null]`, `[null]`}, 66 | {`["a"]`, `[null]`, `[null]`}, 67 | {`["a", "b"]`, `["a", null]`, `["a", null]`}, 68 | {`{"a":["b"]}`, `{"a": ["b", null]}`, `{"a":["b", null]}`}, 69 | {`{"a":[]}`, `{"a": ["b", null, null, "a"]}`, `{"a":["b", null, null, "a"]}`}, 70 | } 71 | 72 | for _, c := range cases { 73 | act := mergePatch(c.original, c.patch) 74 | 75 | if !compareJSON(c.res, act) { 76 | t.Errorf("null values not preserved in array") 77 | } 78 | } 79 | } 80 | 81 | func TestMergePatchRecursesIntoObjects(t *testing.T) { 82 | doc := `{ "person": { "title": "hello", "age": 18 } }` 83 | pat := `{ "person": { "title": "goodbye" } }` 84 | 85 | res := mergePatch(doc, pat) 86 | 87 | exp := `{ "person": { "title": "goodbye", "age": 18 } }` 88 | 89 | if !compareJSON(exp, res) { 90 | t.Fatalf("Key was not replaced") 91 | } 92 | } 93 | 94 | type nonObjectCases struct { 95 | doc, pat, res string 96 | } 97 | 98 | func TestMergePatchReplacesNonObjectsWholesale(t *testing.T) { 99 | a1 := `[1]` 100 | a2 := `[2]` 101 | o1 := `{ "a": 1 }` 102 | o2 := `{ "a": 2 }` 103 | o3 := `{ "a": 1, "b": 1 }` 104 | o4 := `{ "a": 2, "b": 1 }` 105 | 106 | cases := []nonObjectCases{ 107 | {a1, a2, a2}, 108 | {o1, a2, a2}, 109 | {a1, o1, o1}, 110 | {o3, o2, o4}, 111 | } 112 | 113 | for _, c := range cases { 114 | act := mergePatch(c.doc, c.pat) 115 | 116 | if !compareJSON(c.res, act) { 117 | t.Errorf("whole object replacement failed") 118 | } 119 | } 120 | } 121 | 122 | func TestMergePatchReturnsErrorOnBadJSON(t *testing.T) { 123 | _, err := MergePatch([]byte(`[[[[`), []byte(`1`)) 124 | 125 | if err == nil { 126 | t.Errorf("Did not return an error for bad json: %s", err) 127 | } 128 | 129 | _, err = MergePatch([]byte(`1`), []byte(`[[[[`)) 130 | 131 | if err == nil { 132 | t.Errorf("Did not return an error for bad json: %s", err) 133 | } 134 | } 135 | 136 | func TestMergePatchReturnsEmptyArrayOnEmptyArray(t *testing.T) { 137 | doc := `{ "array": ["one", "two"] }` 138 | pat := `{ "array": [] }` 139 | 140 | exp := `{ "array": [] }` 141 | 142 | res, err := MergePatch([]byte(doc), []byte(pat)) 143 | 144 | if err != nil { 145 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 146 | } 147 | 148 | if !compareJSON(exp, string(res)) { 149 | t.Fatalf("Emtpy array did not return not return as empty array") 150 | } 151 | } 152 | 153 | var rfcTests = []struct { 154 | target string 155 | patch string 156 | expected string 157 | }{ 158 | // test cases from https://tools.ietf.org/html/rfc7386#appendix-A 159 | {target: `{"a":"b"}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`}, 160 | {target: `{"a":"b"}`, patch: `{"b":"c"}`, expected: `{"a":"b","b":"c"}`}, 161 | {target: `{"a":"b"}`, patch: `{"a":null}`, expected: `{}`}, 162 | {target: `{"a":"b","b":"c"}`, patch: `{"a":null}`, expected: `{"b":"c"}`}, 163 | {target: `{"a":["b"]}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`}, 164 | {target: `{"a":"c"}`, patch: `{"a":["b"]}`, expected: `{"a":["b"]}`}, 165 | {target: `{"a":{"b": "c"}}`, patch: `{"a": {"b": "d","c": null}}`, expected: `{"a":{"b":"d"}}`}, 166 | {target: `{"a":[{"b":"c"}]}`, patch: `{"a":[1]}`, expected: `{"a":[1]}`}, 167 | {target: `["a","b"]`, patch: `["c","d"]`, expected: `["c","d"]`}, 168 | {target: `{"a":"b"}`, patch: `["c"]`, expected: `["c"]`}, 169 | // {target: `{"a":"foo"}`, patch: `null`, expected: `null`}, 170 | // {target: `{"a":"foo"}`, patch: `"bar"`, expected: `"bar"`}, 171 | {target: `{"e":null}`, patch: `{"a":1}`, expected: `{"a":1,"e":null}`}, 172 | {target: `[1,2]`, patch: `{"a":"b","c":null}`, expected: `{"a":"b"}`}, 173 | {target: `{}`, patch: `{"a":{"bb":{"ccc":null}}}`, expected: `{"a":{"bb":{}}}`}, 174 | } 175 | 176 | func TestMergePatchRFCCases(t *testing.T) { 177 | for i, c := range rfcTests { 178 | out := mergePatch(c.target, c.patch) 179 | 180 | if !compareJSON(out, c.expected) { 181 | t.Errorf("case[%d], patch '%s' did not apply properly to '%s'. expected:\n'%s'\ngot:\n'%s'", i, c.patch, c.target, c.expected, out) 182 | } 183 | } 184 | } 185 | 186 | var rfcFailTests = ` 187 | {"a":"foo"} | null 188 | {"a":"foo"} | "bar" 189 | ` 190 | 191 | func TestMergePatchFailRFCCases(t *testing.T) { 192 | tests := strings.Split(rfcFailTests, "\n") 193 | 194 | for _, c := range tests { 195 | if strings.TrimSpace(c) == "" { 196 | continue 197 | } 198 | 199 | parts := strings.SplitN(c, "|", 2) 200 | 201 | doc := strings.TrimSpace(parts[0]) 202 | pat := strings.TrimSpace(parts[1]) 203 | 204 | out, err := MergePatch([]byte(doc), []byte(pat)) 205 | 206 | if err != ErrBadJSONPatch { 207 | t.Errorf("error not returned properly: %s, %s", err, string(out)) 208 | } 209 | } 210 | 211 | } 212 | 213 | func TestResembleJSONArray(t *testing.T) { 214 | testCases := []struct { 215 | input []byte 216 | expected bool 217 | }{ 218 | // Failure cases 219 | {input: []byte(``), expected: false}, 220 | {input: []byte(`not an array`), expected: false}, 221 | {input: []byte(`{"foo": "bar"}`), expected: false}, 222 | {input: []byte(`{"fizz": ["buzz"]}`), expected: false}, 223 | {input: []byte(`[bad suffix`), expected: false}, 224 | {input: []byte(`bad prefix]`), expected: false}, 225 | {input: []byte(`][`), expected: false}, 226 | 227 | // Valid cases 228 | {input: []byte(`[]`), expected: true}, 229 | {input: []byte(`["foo", "bar"]`), expected: true}, 230 | {input: []byte(`[["foo", "bar"]]`), expected: true}, 231 | {input: []byte(`[not valid syntax]`), expected: true}, 232 | 233 | // Valid cases with whitespace 234 | {input: []byte(` []`), expected: true}, 235 | {input: []byte(`[] `), expected: true}, 236 | {input: []byte(` [] `), expected: true}, 237 | {input: []byte(` [ ] `), expected: true}, 238 | {input: []byte("\t[]"), expected: true}, 239 | {input: []byte("[]\n"), expected: true}, 240 | {input: []byte("\n\t\r[]"), expected: true}, 241 | } 242 | 243 | for _, test := range testCases { 244 | result := resemblesJSONArray(test.input) 245 | if result != test.expected { 246 | t.Errorf( 247 | `expected "%t" but received "%t" for case: "%s"`, 248 | test.expected, 249 | result, 250 | string(test.input), 251 | ) 252 | } 253 | } 254 | } 255 | 256 | func TestCreateMergePatchReplaceKey(t *testing.T) { 257 | doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }` 258 | pat := `{ "title": "goodbye", "nested": {"one": 2, "two": 2} }` 259 | 260 | exp := `{ "title": "goodbye", "nested": {"one": 2} }` 261 | 262 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 263 | 264 | if err != nil { 265 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 266 | } 267 | 268 | if !compareJSON(exp, string(res)) { 269 | t.Fatalf("Key was not replaced") 270 | } 271 | } 272 | 273 | func TestCreateMergePatchGetArray(t *testing.T) { 274 | doc := `{ "title": "hello", "array": ["one", "two"], "notmatch": [1, 2, 3] }` 275 | pat := `{ "title": "hello", "array": ["one", "two", "three"], "notmatch": [1, 2, 3] }` 276 | 277 | exp := `{ "array": ["one", "two", "three"] }` 278 | 279 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 280 | 281 | if err != nil { 282 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 283 | } 284 | 285 | if !compareJSON(exp, string(res)) { 286 | t.Fatalf("Array was not added") 287 | } 288 | } 289 | 290 | func TestCreateMergePatchGetObjArray(t *testing.T) { 291 | doc := `{ "title": "hello", "array": [{"banana": true}, {"evil": false}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }` 292 | pat := `{ "title": "hello", "array": [{"banana": false}, {"evil": true}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }` 293 | 294 | exp := `{ "array": [{"banana": false}, {"evil": true}] }` 295 | 296 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 297 | 298 | if err != nil { 299 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 300 | } 301 | 302 | if !compareJSON(exp, string(res)) { 303 | t.Fatalf("Object array was not added") 304 | } 305 | } 306 | 307 | func TestCreateMergePatchDeleteKey(t *testing.T) { 308 | doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }` 309 | pat := `{ "title": "hello", "nested": {"one": 1} }` 310 | 311 | exp := `{"nested":{"two":null}}` 312 | 313 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 314 | 315 | if err != nil { 316 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 317 | } 318 | 319 | // We cannot use "compareJSON", since Equals does not report a difference if the value is null 320 | if exp != string(res) { 321 | t.Fatalf("Key was not removed") 322 | } 323 | } 324 | 325 | func TestCreateMergePatchEmptyArray(t *testing.T) { 326 | doc := `{ "array": null }` 327 | pat := `{ "array": [] }` 328 | 329 | exp := `{"array":[]}` 330 | 331 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 332 | 333 | if err != nil { 334 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 335 | } 336 | 337 | // We cannot use "compareJSON", since Equals does not report a difference if the value is null 338 | if exp != string(res) { 339 | t.Fatalf("Key was not removed") 340 | } 341 | } 342 | 343 | func TestCreateMergePatchNil(t *testing.T) { 344 | doc := `{ "title": "hello", "nested": {"one": 1, "two": [{"one":null}, {"two":null}, {"three":null}]} }` 345 | pat := doc 346 | 347 | exp := `{}` 348 | 349 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 350 | 351 | if err != nil { 352 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 353 | } 354 | 355 | if !compareJSON(exp, string(res)) { 356 | t.Fatalf("Object array was not added") 357 | } 358 | } 359 | 360 | func TestCreateMergePatchObjArray(t *testing.T) { 361 | doc := `{ "array": [ {"a": {"b": 2}}, {"a": {"b": 3}} ]}` 362 | exp := `{}` 363 | 364 | res, err := CreateMergePatch([]byte(doc), []byte(doc)) 365 | 366 | if err != nil { 367 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 368 | } 369 | 370 | // We cannot use "compareJSON", since Equals does not report a difference if the value is null 371 | if exp != string(res) { 372 | t.Fatalf("Array was not empty, was " + string(res)) 373 | } 374 | } 375 | 376 | func TestCreateMergePatchSameOuterArray(t *testing.T) { 377 | doc := `[{"foo": "bar"}]` 378 | pat := doc 379 | exp := `[{}]` 380 | 381 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 382 | 383 | if err != nil { 384 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 385 | } 386 | 387 | if !compareJSON(exp, string(res)) { 388 | t.Fatalf("Outer array was not unmodified") 389 | } 390 | } 391 | 392 | func TestCreateMergePatchModifiedOuterArray(t *testing.T) { 393 | doc := `[{"name": "John"}, {"name": "Will"}]` 394 | pat := `[{"name": "Jane"}, {"name": "Will"}]` 395 | exp := `[{"name": "Jane"}, {}]` 396 | 397 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 398 | 399 | if err != nil { 400 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 401 | } 402 | 403 | if !compareJSON(exp, string(res)) { 404 | t.Fatalf("Expected %s but received %s", exp, res) 405 | } 406 | } 407 | 408 | func TestCreateMergePatchMismatchedOuterArray(t *testing.T) { 409 | doc := `[{"name": "John"}, {"name": "Will"}]` 410 | pat := `[{"name": "Jane"}]` 411 | 412 | _, err := CreateMergePatch([]byte(doc), []byte(pat)) 413 | 414 | if err == nil { 415 | t.Errorf("Expected error due to array length differences but received none") 416 | } 417 | } 418 | 419 | func TestCreateMergePatchMismatchedOuterTypes(t *testing.T) { 420 | doc := `[{"name": "John"}]` 421 | pat := `{"name": "Jane"}` 422 | 423 | _, err := CreateMergePatch([]byte(doc), []byte(pat)) 424 | 425 | if err == nil { 426 | t.Errorf("Expected error due to mismatched types but received none") 427 | } 428 | } 429 | 430 | func TestCreateMergePatchNoDifferences(t *testing.T) { 431 | doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }` 432 | pat := doc 433 | 434 | exp := `{}` 435 | 436 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 437 | 438 | if err != nil { 439 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 440 | } 441 | 442 | if !compareJSON(exp, string(res)) { 443 | t.Fatalf("Key was not replaced") 444 | } 445 | } 446 | 447 | func TestCreateMergePatchComplexMatch(t *testing.T) { 448 | doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }` 449 | empty := `{}` 450 | res, err := CreateMergePatch([]byte(doc), []byte(doc)) 451 | 452 | if err != nil { 453 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 454 | } 455 | 456 | // We cannot use "compareJSON", since Equals does not report a difference if the value is null 457 | if empty != string(res) { 458 | t.Fatalf("Did not get empty result, was:%s", string(res)) 459 | } 460 | } 461 | 462 | func TestCreateMergePatchComplexAddAll(t *testing.T) { 463 | doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }` 464 | empty := `{}` 465 | res, err := CreateMergePatch([]byte(empty), []byte(doc)) 466 | 467 | if err != nil { 468 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 469 | } 470 | 471 | if !compareJSON(doc, string(res)) { 472 | t.Fatalf("Did not get everything as, it was:\n%s", string(res)) 473 | } 474 | } 475 | 476 | // createNestedMap created a series of nested map objects such that the number of 477 | // objects is roughly 2^n (precisely, 2^(n+1)-1). 478 | func createNestedMap(m map[string]interface{}, depth int, objectCount *int) { 479 | if depth == 0 { 480 | return 481 | } 482 | for i := 0; i < 2; i++ { 483 | nested := map[string]interface{}{} 484 | *objectCount += 1 485 | createNestedMap(nested, depth-1, objectCount) 486 | m[fmt.Sprintf("key-%v", i)] = nested 487 | } 488 | } 489 | 490 | func TestMatchesValue(t *testing.T) { 491 | testcases := []struct { 492 | name string 493 | a interface{} 494 | b interface{} 495 | want bool 496 | }{ 497 | { 498 | name: "map empty", 499 | a: map[string]interface{}{}, 500 | b: map[string]interface{}{}, 501 | want: true, 502 | }, 503 | { 504 | name: "map equal keys, equal non-nil value", 505 | a: map[string]interface{}{"1": true}, 506 | b: map[string]interface{}{"1": true}, 507 | want: true, 508 | }, 509 | { 510 | name: "map equal keys, equal nil value", 511 | a: map[string]interface{}{"1": nil}, 512 | b: map[string]interface{}{"1": nil}, 513 | want: true, 514 | }, 515 | 516 | { 517 | name: "map different value", 518 | a: map[string]interface{}{"1": true}, 519 | b: map[string]interface{}{"1": false}, 520 | want: false, 521 | }, 522 | { 523 | name: "map different key, matching non-nil value", 524 | a: map[string]interface{}{"1": true}, 525 | b: map[string]interface{}{"2": true}, 526 | want: false, 527 | }, 528 | { 529 | name: "map different key, matching nil value", 530 | a: map[string]interface{}{"1": nil}, 531 | b: map[string]interface{}{"2": nil}, 532 | want: false, 533 | }, 534 | { 535 | name: "map different key, first nil value", 536 | a: map[string]interface{}{"1": true}, 537 | b: map[string]interface{}{"2": nil}, 538 | want: false, 539 | }, 540 | { 541 | name: "map different key, second nil value", 542 | a: map[string]interface{}{"1": nil}, 543 | b: map[string]interface{}{"2": true}, 544 | want: false, 545 | }, 546 | } 547 | for _, tc := range testcases { 548 | t.Run(tc.name, func(t *testing.T) { 549 | got := matchesValue(tc.a, tc.b) 550 | if got != tc.want { 551 | t.Fatalf("want %v, got %v", tc.want, got) 552 | } 553 | }) 554 | } 555 | } 556 | 557 | func benchmarkMatchesValueWithDeeplyNestedFields(depth int, b *testing.B) { 558 | a := map[string]interface{}{} 559 | objCount := 1 560 | createNestedMap(a, depth, &objCount) 561 | b.ResetTimer() 562 | b.Run(fmt.Sprintf("objectCount=%v", objCount), func(b *testing.B) { 563 | for i := 0; i < b.N; i++ { 564 | if !matchesValue(a, a) { 565 | b.Errorf("Should be equal") 566 | } 567 | } 568 | }) 569 | } 570 | 571 | func BenchmarkMatchesValue1(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(1, b) } 572 | func BenchmarkMatchesValue2(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(2, b) } 573 | func BenchmarkMatchesValue3(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(3, b) } 574 | func BenchmarkMatchesValue4(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(4, b) } 575 | func BenchmarkMatchesValue5(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(5, b) } 576 | func BenchmarkMatchesValue6(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(6, b) } 577 | func BenchmarkMatchesValue7(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(7, b) } 578 | func BenchmarkMatchesValue8(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(8, b) } 579 | func BenchmarkMatchesValue9(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(9, b) } 580 | func BenchmarkMatchesValue10(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(10, b) } 581 | 582 | func TestCreateMergePatchComplexRemoveAll(t *testing.T) { 583 | doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }` 584 | exp := `{"a":null,"f":null,"hello":null,"i":null,"n":null,"nested":null,"pi":null,"t":null}` 585 | empty := `{}` 586 | res, err := CreateMergePatch([]byte(doc), []byte(empty)) 587 | 588 | if err != nil { 589 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 590 | } 591 | 592 | if exp != string(res) { 593 | t.Fatalf("Did not get result, was:%s", string(res)) 594 | } 595 | 596 | // FIXME: Crashes if using compareJSON like this: 597 | /* 598 | if !compareJSON(doc, string(res)) { 599 | t.Fatalf("Did not get everything as, it was:\n%s", string(res)) 600 | } 601 | */ 602 | } 603 | 604 | func TestCreateMergePatchObjectWithInnerArray(t *testing.T) { 605 | stateString := `{ 606 | "OuterArray": [ 607 | { 608 | "InnerArray": [ 609 | { 610 | "StringAttr": "abc123" 611 | } 612 | ], 613 | "StringAttr": "def456" 614 | } 615 | ] 616 | }` 617 | 618 | patch, err := CreateMergePatch([]byte(stateString), []byte(stateString)) 619 | if err != nil { 620 | t.Fatal(err) 621 | } 622 | 623 | if string(patch) != "{}" { 624 | t.Fatalf("Patch should have been {} but was: %v", string(patch)) 625 | } 626 | } 627 | 628 | func TestCreateMergePatchReplaceKeyNotEscape(t *testing.T) { 629 | doc := `{ "title": "hello", "nested": {"title/escaped": 1, "two": 2} }` 630 | pat := `{ "title": "goodbye", "nested": {"title/escaped": 2, "two": 2} }` 631 | 632 | exp := `{ "title": "goodbye", "nested": {"title/escaped": 2} }` 633 | 634 | res, err := CreateMergePatch([]byte(doc), []byte(pat)) 635 | 636 | if err != nil { 637 | t.Errorf("Unexpected error: %s, %s", err, string(res)) 638 | } 639 | 640 | if !compareJSON(exp, string(res)) { 641 | t.Log(string(res)) 642 | t.Fatalf("Key was not replaced") 643 | } 644 | } 645 | 646 | func TestMergePatchReplaceKeyNotEscaping(t *testing.T) { 647 | doc := `{ "obj": { "title/escaped": "hello" } }` 648 | pat := `{ "obj": { "title/escaped": "goodbye" } }` 649 | exp := `{ "obj": { "title/escaped": "goodbye" } }` 650 | 651 | res := mergePatch(doc, pat) 652 | 653 | if !compareJSON(exp, res) { 654 | t.Fatalf("Key was not replaced") 655 | } 656 | } 657 | 658 | func TestMergeMergePatches(t *testing.T) { 659 | cases := []struct { 660 | demonstrates string 661 | p1 string 662 | p2 string 663 | exp string 664 | }{ 665 | { 666 | demonstrates: "simple patches are merged normally", 667 | p1: `{"add1": 1}`, 668 | p2: `{"add2": 2}`, 669 | exp: `{"add1": 1, "add2": 2}`, 670 | }, 671 | { 672 | demonstrates: "nulls are kept", 673 | p1: `{"del1": null}`, 674 | p2: `{"del2": null}`, 675 | exp: `{"del1": null, "del2": null}`, 676 | }, 677 | { 678 | demonstrates: "nulls are kept in complex objects", 679 | p1: `{}`, 680 | p2: `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`, 681 | exp: `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`, 682 | }, 683 | { 684 | demonstrates: "a key added then deleted is kept deleted", 685 | p1: `{"add_then_delete": "atd"}`, 686 | p2: `{"add_then_delete": null}`, 687 | exp: `{"add_then_delete": null}`, 688 | }, 689 | { 690 | demonstrates: "a key deleted then added is kept added", 691 | p1: `{"delete_then_add": null}`, 692 | p2: `{"delete_then_add": "dta"}`, 693 | exp: `{"delete_then_add": "dta"}`, 694 | }, 695 | { 696 | demonstrates: "object overrides array", 697 | p1: `[]`, 698 | p2: `{"del": null, "add": "a"}`, 699 | exp: `{"del": null, "add": "a"}`, 700 | }, 701 | { 702 | demonstrates: "array overrides object", 703 | p1: `{"del": null, "add": "a"}`, 704 | p2: `[]`, 705 | exp: `[]`, 706 | }, 707 | } 708 | 709 | for _, c := range cases { 710 | out, err := MergeMergePatches([]byte(c.p1), []byte(c.p2)) 711 | 712 | if err != nil { 713 | panic(err) 714 | } 715 | 716 | if !compareJSON(c.exp, string(out)) { 717 | t.Logf("Error while trying to demonstrate: %v", c.demonstrates) 718 | t.Logf("Got %v", string(out)) 719 | t.Logf("Expected %v", c.exp) 720 | t.Fatalf("Merged merge patch is incorrect") 721 | } 722 | } 723 | } 724 | -------------------------------------------------------------------------------- /patch.go: -------------------------------------------------------------------------------- 1 | package jsonpatch 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | const ( 13 | eRaw = iota 14 | eDoc 15 | eAry 16 | ) 17 | 18 | var ( 19 | // SupportNegativeIndices decides whether to support non-standard practice of 20 | // allowing negative indices to mean indices starting at the end of an array. 21 | // Default to true. 22 | SupportNegativeIndices bool = true 23 | // AccumulatedCopySizeLimit limits the total size increase in bytes caused by 24 | // "copy" operations in a patch. 25 | AccumulatedCopySizeLimit int64 = 0 26 | ) 27 | 28 | var ( 29 | ErrTestFailed = errors.New("test failed") 30 | ErrMissing = errors.New("missing value") 31 | ErrUnknownType = errors.New("unknown object type") 32 | ErrInvalid = errors.New("invalid state detected") 33 | ErrInvalidIndex = errors.New("invalid index referenced") 34 | ) 35 | 36 | type lazyNode struct { 37 | raw *json.RawMessage 38 | doc partialDoc 39 | ary partialArray 40 | which int 41 | } 42 | 43 | // Operation is a single JSON-Patch step, such as a single 'add' operation. 44 | type Operation map[string]*json.RawMessage 45 | 46 | // Patch is an ordered collection of Operations. 47 | type Patch []Operation 48 | 49 | type partialDoc map[string]*lazyNode 50 | type partialArray []*lazyNode 51 | 52 | type container interface { 53 | get(key string) (*lazyNode, error) 54 | set(key string, val *lazyNode) error 55 | add(key string, val *lazyNode) error 56 | remove(key string) error 57 | } 58 | 59 | func newLazyNode(raw *json.RawMessage) *lazyNode { 60 | return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw} 61 | } 62 | 63 | func (n *lazyNode) MarshalJSON() ([]byte, error) { 64 | switch n.which { 65 | case eRaw: 66 | return json.Marshal(n.raw) 67 | case eDoc: 68 | return json.Marshal(n.doc) 69 | case eAry: 70 | return json.Marshal(n.ary) 71 | default: 72 | return nil, ErrUnknownType 73 | } 74 | } 75 | 76 | func (n *lazyNode) UnmarshalJSON(data []byte) error { 77 | dest := make(json.RawMessage, len(data)) 78 | copy(dest, data) 79 | n.raw = &dest 80 | n.which = eRaw 81 | return nil 82 | } 83 | 84 | func deepCopy(src *lazyNode) (*lazyNode, int, error) { 85 | if src == nil { 86 | return nil, 0, nil 87 | } 88 | a, err := src.MarshalJSON() 89 | if err != nil { 90 | return nil, 0, err 91 | } 92 | sz := len(a) 93 | ra := make(json.RawMessage, sz) 94 | copy(ra, a) 95 | return newLazyNode(&ra), sz, nil 96 | } 97 | 98 | func (n *lazyNode) intoDoc() (*partialDoc, error) { 99 | if n.which == eDoc { 100 | return &n.doc, nil 101 | } 102 | 103 | if n.raw == nil { 104 | return nil, ErrInvalid 105 | } 106 | 107 | err := json.Unmarshal(*n.raw, &n.doc) 108 | 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | n.which = eDoc 114 | return &n.doc, nil 115 | } 116 | 117 | func (n *lazyNode) intoAry() (*partialArray, error) { 118 | if n.which == eAry { 119 | return &n.ary, nil 120 | } 121 | 122 | if n.raw == nil { 123 | return nil, ErrInvalid 124 | } 125 | 126 | err := json.Unmarshal(*n.raw, &n.ary) 127 | 128 | if err != nil { 129 | return nil, err 130 | } 131 | 132 | n.which = eAry 133 | return &n.ary, nil 134 | } 135 | 136 | func (n *lazyNode) compact() []byte { 137 | buf := &bytes.Buffer{} 138 | 139 | if n.raw == nil { 140 | return nil 141 | } 142 | 143 | err := json.Compact(buf, *n.raw) 144 | 145 | if err != nil { 146 | return *n.raw 147 | } 148 | 149 | return buf.Bytes() 150 | } 151 | 152 | func (n *lazyNode) tryDoc() bool { 153 | if n.raw == nil { 154 | return false 155 | } 156 | 157 | err := json.Unmarshal(*n.raw, &n.doc) 158 | 159 | if err != nil { 160 | return false 161 | } 162 | 163 | n.which = eDoc 164 | return true 165 | } 166 | 167 | func (n *lazyNode) tryAry() bool { 168 | if n.raw == nil { 169 | return false 170 | } 171 | 172 | err := json.Unmarshal(*n.raw, &n.ary) 173 | 174 | if err != nil { 175 | return false 176 | } 177 | 178 | n.which = eAry 179 | return true 180 | } 181 | 182 | func (n *lazyNode) equal(o *lazyNode) bool { 183 | if n.which == eRaw { 184 | if !n.tryDoc() && !n.tryAry() { 185 | if o.which != eRaw { 186 | return false 187 | } 188 | 189 | return bytes.Equal(n.compact(), o.compact()) 190 | } 191 | } 192 | 193 | if n.which == eDoc { 194 | if o.which == eRaw { 195 | if !o.tryDoc() { 196 | return false 197 | } 198 | } 199 | 200 | if o.which != eDoc { 201 | return false 202 | } 203 | 204 | if len(n.doc) != len(o.doc) { 205 | return false 206 | } 207 | 208 | for k, v := range n.doc { 209 | ov, ok := o.doc[k] 210 | 211 | if !ok { 212 | return false 213 | } 214 | 215 | if (v == nil) != (ov == nil) { 216 | return false 217 | } 218 | 219 | if v == nil && ov == nil { 220 | continue 221 | } 222 | 223 | if !v.equal(ov) { 224 | return false 225 | } 226 | } 227 | 228 | return true 229 | } 230 | 231 | if o.which != eAry && !o.tryAry() { 232 | return false 233 | } 234 | 235 | if len(n.ary) != len(o.ary) { 236 | return false 237 | } 238 | 239 | for idx, val := range n.ary { 240 | if !val.equal(o.ary[idx]) { 241 | return false 242 | } 243 | } 244 | 245 | return true 246 | } 247 | 248 | // Kind reads the "op" field of the Operation. 249 | func (o Operation) Kind() string { 250 | if obj, ok := o["op"]; ok && obj != nil { 251 | var op string 252 | 253 | err := json.Unmarshal(*obj, &op) 254 | 255 | if err != nil { 256 | return "unknown" 257 | } 258 | 259 | return op 260 | } 261 | 262 | return "unknown" 263 | } 264 | 265 | // Path reads the "path" field of the Operation. 266 | func (o Operation) Path() (string, error) { 267 | if obj, ok := o["path"]; ok && obj != nil { 268 | var op string 269 | 270 | err := json.Unmarshal(*obj, &op) 271 | 272 | if err != nil { 273 | return "unknown", err 274 | } 275 | 276 | return op, nil 277 | } 278 | 279 | return "unknown", fmt.Errorf("operation missing path field: %w", ErrMissing) 280 | } 281 | 282 | // From reads the "from" field of the Operation. 283 | func (o Operation) From() (string, error) { 284 | if obj, ok := o["from"]; ok && obj != nil { 285 | var op string 286 | 287 | err := json.Unmarshal(*obj, &op) 288 | 289 | if err != nil { 290 | return "unknown", err 291 | } 292 | 293 | return op, nil 294 | } 295 | 296 | return "unknown", fmt.Errorf("operation, missing from field: %w", ErrMissing) 297 | } 298 | 299 | func (o Operation) value() *lazyNode { 300 | if obj, ok := o["value"]; ok { 301 | return newLazyNode(obj) 302 | } 303 | 304 | return nil 305 | } 306 | 307 | // ValueInterface decodes the operation value into an interface. 308 | func (o Operation) ValueInterface() (interface{}, error) { 309 | if obj, ok := o["value"]; ok && obj != nil { 310 | var v interface{} 311 | 312 | err := json.Unmarshal(*obj, &v) 313 | 314 | if err != nil { 315 | return nil, err 316 | } 317 | 318 | return v, nil 319 | } 320 | 321 | return nil, fmt.Errorf("operation, missing value field: %w", ErrMissing) 322 | } 323 | 324 | func isArray(buf []byte) bool { 325 | Loop: 326 | for _, c := range buf { 327 | switch c { 328 | case ' ': 329 | case '\n': 330 | case '\t': 331 | continue 332 | case '[': 333 | return true 334 | default: 335 | break Loop 336 | } 337 | } 338 | 339 | return false 340 | } 341 | 342 | func findObject(pd *container, path string) (container, string) { 343 | doc := *pd 344 | 345 | split := strings.Split(path, "/") 346 | 347 | if len(split) < 2 { 348 | return nil, "" 349 | } 350 | 351 | parts := split[1 : len(split)-1] 352 | 353 | key := split[len(split)-1] 354 | 355 | var err error 356 | 357 | for _, part := range parts { 358 | 359 | next, ok := doc.get(decodePatchKey(part)) 360 | 361 | if next == nil || ok != nil || next.raw == nil { 362 | return nil, "" 363 | } 364 | 365 | if isArray(*next.raw) { 366 | doc, err = next.intoAry() 367 | 368 | if err != nil { 369 | return nil, "" 370 | } 371 | } else { 372 | doc, err = next.intoDoc() 373 | 374 | if err != nil { 375 | return nil, "" 376 | } 377 | } 378 | } 379 | 380 | return doc, decodePatchKey(key) 381 | } 382 | 383 | func (d *partialDoc) set(key string, val *lazyNode) error { 384 | (*d)[key] = val 385 | return nil 386 | } 387 | 388 | func (d *partialDoc) add(key string, val *lazyNode) error { 389 | (*d)[key] = val 390 | return nil 391 | } 392 | 393 | func (d *partialDoc) get(key string) (*lazyNode, error) { 394 | return (*d)[key], nil 395 | } 396 | 397 | func (d *partialDoc) remove(key string) error { 398 | _, ok := (*d)[key] 399 | if !ok { 400 | return fmt.Errorf("Unable to remove nonexistent key: %s: %w", key, ErrMissing) 401 | } 402 | 403 | delete(*d, key) 404 | return nil 405 | } 406 | 407 | // set should only be used to implement the "replace" operation, so "key" must 408 | // be an already existing index in "d". 409 | func (d *partialArray) set(key string, val *lazyNode) error { 410 | idx, err := strconv.Atoi(key) 411 | if err != nil { 412 | return err 413 | } 414 | 415 | if idx < 0 { 416 | if !SupportNegativeIndices { 417 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 418 | } 419 | if idx < -len(*d) { 420 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 421 | } 422 | idx += len(*d) 423 | } 424 | 425 | (*d)[idx] = val 426 | return nil 427 | } 428 | 429 | func (d *partialArray) add(key string, val *lazyNode) error { 430 | if key == "-" { 431 | *d = append(*d, val) 432 | return nil 433 | } 434 | 435 | idx, err := strconv.Atoi(key) 436 | if err != nil { 437 | return fmt.Errorf("value was not a proper array index: '%s': %w", key, err) 438 | } 439 | 440 | sz := len(*d) + 1 441 | 442 | ary := make([]*lazyNode, sz) 443 | 444 | cur := *d 445 | 446 | if idx >= len(ary) { 447 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 448 | } 449 | 450 | if idx < 0 { 451 | if !SupportNegativeIndices { 452 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 453 | } 454 | if idx < -len(ary) { 455 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 456 | } 457 | idx += len(ary) 458 | } 459 | 460 | copy(ary[0:idx], cur[0:idx]) 461 | ary[idx] = val 462 | copy(ary[idx+1:], cur[idx:]) 463 | 464 | *d = ary 465 | return nil 466 | } 467 | 468 | func (d *partialArray) get(key string) (*lazyNode, error) { 469 | idx, err := strconv.Atoi(key) 470 | 471 | if err != nil { 472 | return nil, err 473 | } 474 | 475 | if idx < 0 { 476 | if !SupportNegativeIndices { 477 | return nil, fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 478 | } 479 | if idx < -len(*d) { 480 | return nil, fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 481 | } 482 | idx += len(*d) 483 | } 484 | 485 | if idx >= len(*d) { 486 | return nil, fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 487 | } 488 | 489 | return (*d)[idx], nil 490 | } 491 | 492 | func (d *partialArray) remove(key string) error { 493 | idx, err := strconv.Atoi(key) 494 | if err != nil { 495 | return err 496 | } 497 | 498 | cur := *d 499 | 500 | if idx >= len(cur) { 501 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 502 | } 503 | 504 | if idx < 0 { 505 | if !SupportNegativeIndices { 506 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 507 | } 508 | if idx < -len(cur) { 509 | return fmt.Errorf("Unable to access invalid index: %d: %w", idx, ErrInvalidIndex) 510 | } 511 | idx += len(cur) 512 | } 513 | 514 | ary := make([]*lazyNode, len(cur)-1) 515 | 516 | copy(ary[0:idx], cur[0:idx]) 517 | copy(ary[idx:], cur[idx+1:]) 518 | 519 | *d = ary 520 | return nil 521 | 522 | } 523 | 524 | func (p Patch) add(doc *container, op Operation) error { 525 | path, err := op.Path() 526 | if err != nil { 527 | return fmt.Errorf("add operation failed to decode path: %w", ErrMissing) 528 | } 529 | 530 | con, key := findObject(doc, path) 531 | 532 | if con == nil { 533 | return fmt.Errorf("add operation does not apply: doc is missing path: \"%s\": %w", path, ErrMissing) 534 | } 535 | 536 | err = con.add(key, op.value()) 537 | if err != nil { 538 | return fmt.Errorf("error in add for path: '%s': %w", path, err) 539 | } 540 | 541 | return nil 542 | } 543 | 544 | func (p Patch) remove(doc *container, op Operation) error { 545 | path, err := op.Path() 546 | if err != nil { 547 | return fmt.Errorf("remove operation failed to decode path: %w", ErrMissing) 548 | } 549 | 550 | con, key := findObject(doc, path) 551 | 552 | if con == nil { 553 | return fmt.Errorf("remove operation does not apply: doc is missing path: \"%s\": %w", path, ErrMissing) 554 | } 555 | 556 | err = con.remove(key) 557 | if err != nil { 558 | return fmt.Errorf("error in remove for path: '%s': %w", path, err) 559 | } 560 | 561 | return nil 562 | } 563 | 564 | func (p Patch) replace(doc *container, op Operation) error { 565 | path, err := op.Path() 566 | if err != nil { 567 | return fmt.Errorf("replace operation failed to decode path: %w", err) 568 | } 569 | 570 | if path == "" { 571 | val := op.value() 572 | 573 | if val.which == eRaw { 574 | if !val.tryDoc() { 575 | if !val.tryAry() { 576 | return fmt.Errorf("replace operation value must be object or array: %w", err) 577 | } 578 | } 579 | } 580 | 581 | switch val.which { 582 | case eAry: 583 | *doc = &val.ary 584 | case eDoc: 585 | *doc = &val.doc 586 | case eRaw: 587 | return fmt.Errorf("replace operation hit impossible case: %w", err) 588 | } 589 | 590 | return nil 591 | } 592 | 593 | con, key := findObject(doc, path) 594 | 595 | if con == nil { 596 | return fmt.Errorf("replace operation does not apply: doc is missing path: %s: %w", path, ErrMissing) 597 | } 598 | 599 | _, ok := con.get(key) 600 | if ok != nil { 601 | return fmt.Errorf("replace operation does not apply: doc is missing key: %s: %w", path, ErrMissing) 602 | } 603 | 604 | err = con.set(key, op.value()) 605 | if err != nil { 606 | return fmt.Errorf("error in remove for path: '%s': %w", path, err) 607 | } 608 | 609 | return nil 610 | } 611 | 612 | func (p Patch) move(doc *container, op Operation) error { 613 | from, err := op.From() 614 | if err != nil { 615 | return fmt.Errorf("move operation failed to decode from: %w", err) 616 | } 617 | 618 | con, key := findObject(doc, from) 619 | 620 | if con == nil { 621 | return fmt.Errorf("move operation does not apply: doc is missing from path: %s: %w", from, ErrMissing) 622 | } 623 | 624 | val, err := con.get(key) 625 | if err != nil { 626 | return fmt.Errorf("error in move for path: '%s': %w", key, err) 627 | } 628 | 629 | err = con.remove(key) 630 | if err != nil { 631 | return fmt.Errorf("error in move for path: '%s': %w", key, err) 632 | } 633 | 634 | path, err := op.Path() 635 | if err != nil { 636 | return fmt.Errorf("move operation failed to decode path: %w", err) 637 | } 638 | 639 | con, key = findObject(doc, path) 640 | 641 | if con == nil { 642 | return fmt.Errorf("move operation does not apply: doc is missing destination path: %s: %w", path, ErrMissing) 643 | } 644 | 645 | err = con.add(key, val) 646 | if err != nil { 647 | return fmt.Errorf("error in move for path: '%s': %w", path, err) 648 | } 649 | 650 | return nil 651 | } 652 | 653 | func (p Patch) test(doc *container, op Operation) error { 654 | path, err := op.Path() 655 | if err != nil { 656 | return fmt.Errorf("test operation failed to decode path: %w", err) 657 | } 658 | 659 | if path == "" { 660 | var self lazyNode 661 | 662 | switch sv := (*doc).(type) { 663 | case *partialDoc: 664 | self.doc = *sv 665 | self.which = eDoc 666 | case *partialArray: 667 | self.ary = *sv 668 | self.which = eAry 669 | } 670 | 671 | if self.equal(op.value()) { 672 | return nil 673 | } 674 | 675 | return fmt.Errorf("testing value %s failed: %w", path, ErrTestFailed) 676 | } 677 | 678 | con, key := findObject(doc, path) 679 | 680 | if con == nil { 681 | return fmt.Errorf("test operation does not apply: is missing path: %s: %w", path, ErrMissing) 682 | } 683 | 684 | val, err := con.get(key) 685 | if err != nil { 686 | return fmt.Errorf("error in test for path: '%s': %w", path, err) 687 | } 688 | 689 | if val == nil { 690 | if op.value() == nil || op.value().raw == nil { 691 | return nil 692 | } 693 | return fmt.Errorf("testing value %s failed: %w", path, ErrTestFailed) 694 | } else if op.value() == nil { 695 | return fmt.Errorf("testing value %s failed: %w", path, ErrTestFailed) 696 | } 697 | 698 | if val.equal(op.value()) { 699 | return nil 700 | } 701 | 702 | return fmt.Errorf("testing value %s failed: %w", path, ErrTestFailed) 703 | } 704 | 705 | func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) error { 706 | from, err := op.From() 707 | if err != nil { 708 | return fmt.Errorf("copy operation failed to decode from: %w", err) 709 | } 710 | 711 | con, key := findObject(doc, from) 712 | 713 | if con == nil { 714 | return fmt.Errorf("copy operation does not apply: doc is missing from path: %s: %w", from, ErrMissing) 715 | } 716 | 717 | val, err := con.get(key) 718 | if err != nil { 719 | return fmt.Errorf("error in copy for from: '%s': %w", from, err) 720 | } 721 | 722 | path, err := op.Path() 723 | if err != nil { 724 | return fmt.Errorf("copy operation failed to decode path: %w", ErrMissing) 725 | } 726 | 727 | con, key = findObject(doc, path) 728 | 729 | if con == nil { 730 | return fmt.Errorf("copy operation does not apply: doc is missing destination path: %s: %w", path, ErrMissing) 731 | } 732 | 733 | valCopy, sz, err := deepCopy(val) 734 | if err != nil { 735 | return fmt.Errorf("error while performing deep copy: %w", err) 736 | } 737 | 738 | (*accumulatedCopySize) += int64(sz) 739 | if AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > AccumulatedCopySizeLimit { 740 | return NewAccumulatedCopySizeError(AccumulatedCopySizeLimit, *accumulatedCopySize) 741 | } 742 | 743 | err = con.add(key, valCopy) 744 | if err != nil { 745 | return fmt.Errorf("error while adding value during copy: %w", err) 746 | } 747 | 748 | return nil 749 | } 750 | 751 | // Equal indicates if 2 JSON documents have the same structural equality. 752 | func Equal(a, b []byte) bool { 753 | ra := make(json.RawMessage, len(a)) 754 | copy(ra, a) 755 | la := newLazyNode(&ra) 756 | 757 | rb := make(json.RawMessage, len(b)) 758 | copy(rb, b) 759 | lb := newLazyNode(&rb) 760 | 761 | return la.equal(lb) 762 | } 763 | 764 | // DecodePatch decodes the passed JSON document as an RFC 6902 patch. 765 | func DecodePatch(buf []byte) (Patch, error) { 766 | var p Patch 767 | 768 | err := json.Unmarshal(buf, &p) 769 | 770 | if err != nil { 771 | return nil, err 772 | } 773 | 774 | return p, nil 775 | } 776 | 777 | // Apply mutates a JSON document according to the patch, and returns the new 778 | // document. 779 | func (p Patch) Apply(doc []byte) ([]byte, error) { 780 | return p.ApplyIndent(doc, "") 781 | } 782 | 783 | // ApplyIndent mutates a JSON document according to the patch, and returns the new 784 | // document indented. 785 | func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { 786 | if len(doc) == 0 { 787 | return doc, nil 788 | } 789 | 790 | var pd container 791 | if doc[0] == '[' { 792 | pd = &partialArray{} 793 | } else { 794 | pd = &partialDoc{} 795 | } 796 | 797 | err := json.Unmarshal(doc, pd) 798 | 799 | if err != nil { 800 | return nil, err 801 | } 802 | 803 | err = nil 804 | 805 | var accumulatedCopySize int64 806 | 807 | for _, op := range p { 808 | switch op.Kind() { 809 | case "add": 810 | err = p.add(&pd, op) 811 | case "remove": 812 | err = p.remove(&pd, op) 813 | case "replace": 814 | err = p.replace(&pd, op) 815 | case "move": 816 | err = p.move(&pd, op) 817 | case "test": 818 | err = p.test(&pd, op) 819 | case "copy": 820 | err = p.copy(&pd, op, &accumulatedCopySize) 821 | default: 822 | err = fmt.Errorf("Unexpected kind: %s", op.Kind()) 823 | } 824 | 825 | if err != nil { 826 | return nil, err 827 | } 828 | } 829 | 830 | if indent != "" { 831 | return json.MarshalIndent(pd, "", indent) 832 | } 833 | 834 | return json.Marshal(pd) 835 | } 836 | 837 | // From http://tools.ietf.org/html/rfc6901#section-4 : 838 | // 839 | // Evaluation of each reference token begins by decoding any escaped 840 | // character sequence. This is performed by first transforming any 841 | // occurrence of the sequence '~1' to '/', and then transforming any 842 | // occurrence of the sequence '~0' to '~'. 843 | 844 | var ( 845 | rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~") 846 | ) 847 | 848 | func decodePatchKey(k string) string { 849 | return rfc6901Decoder.Replace(k) 850 | } 851 | -------------------------------------------------------------------------------- /patch_test.go: -------------------------------------------------------------------------------- 1 | package jsonpatch 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func reformatJSON(j string) string { 12 | buf := new(bytes.Buffer) 13 | 14 | json.Indent(buf, []byte(j), "", " ") 15 | 16 | return buf.String() 17 | } 18 | 19 | func compareJSON(a, b string) bool { 20 | // return Equal([]byte(a), []byte(b)) 21 | 22 | var objA, objB map[string]interface{} 23 | json.Unmarshal([]byte(a), &objA) 24 | json.Unmarshal([]byte(b), &objB) 25 | 26 | // fmt.Printf("Comparing %#v\nagainst %#v\n", objA, objB) 27 | return reflect.DeepEqual(objA, objB) 28 | } 29 | 30 | func applyPatch(doc, patch string) (string, error) { 31 | obj, err := DecodePatch([]byte(patch)) 32 | 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | out, err := obj.Apply([]byte(doc)) 38 | 39 | if err != nil { 40 | return "", err 41 | } 42 | 43 | return string(out), nil 44 | } 45 | 46 | type Case struct { 47 | doc, patch, result string 48 | } 49 | 50 | func repeatedA(r int) string { 51 | var s string 52 | for i := 0; i < r; i++ { 53 | s += "A" 54 | } 55 | return s 56 | } 57 | 58 | var Cases = []Case{ 59 | { 60 | ``, 61 | `[ 62 | { "op": "add", "path": "/baz", "value": "qux" } 63 | ]`, 64 | ``, 65 | }, 66 | { 67 | `{ "foo": "bar"}`, 68 | `[ 69 | { "op": "add", "path": "/baz", "value": "qux" } 70 | ]`, 71 | `{ 72 | "baz": "qux", 73 | "foo": "bar" 74 | }`, 75 | }, 76 | { 77 | `{ "foo": [ "bar", "baz" ] }`, 78 | `[ 79 | { "op": "add", "path": "/foo/1", "value": "qux" } 80 | ]`, 81 | `{ "foo": [ "bar", "qux", "baz" ] }`, 82 | }, 83 | { 84 | `{ "foo": [ "bar", "baz" ] }`, 85 | `[ 86 | { "op": "add", "path": "/foo/-1", "value": "qux" } 87 | ]`, 88 | `{ "foo": [ "bar", "baz", "qux" ] }`, 89 | }, 90 | { 91 | `{ "baz": "qux", "foo": "bar" }`, 92 | `[ { "op": "remove", "path": "/baz" } ]`, 93 | `{ "foo": "bar" }`, 94 | }, 95 | { 96 | `{ "foo": [ "bar", "qux", "baz" ] }`, 97 | `[ { "op": "remove", "path": "/foo/1" } ]`, 98 | `{ "foo": [ "bar", "baz" ] }`, 99 | }, 100 | { 101 | `{ "baz": "qux", "foo": "bar" }`, 102 | `[ { "op": "replace", "path": "/baz", "value": "boo" } ]`, 103 | `{ "baz": "boo", "foo": "bar" }`, 104 | }, 105 | { 106 | `{ 107 | "foo": { 108 | "bar": "baz", 109 | "waldo": "fred" 110 | }, 111 | "qux": { 112 | "corge": "grault" 113 | } 114 | }`, 115 | `[ { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } ]`, 116 | `{ 117 | "foo": { 118 | "bar": "baz" 119 | }, 120 | "qux": { 121 | "corge": "grault", 122 | "thud": "fred" 123 | } 124 | }`, 125 | }, 126 | { 127 | `{ "foo": [ "all", "grass", "cows", "eat" ] }`, 128 | `[ { "op": "move", "from": "/foo/1", "path": "/foo/3" } ]`, 129 | `{ "foo": [ "all", "cows", "eat", "grass" ] }`, 130 | }, 131 | { 132 | `{ "foo": [ "all", "grass", "cows", "eat" ] }`, 133 | `[ { "op": "move", "from": "/foo/1", "path": "/foo/2" } ]`, 134 | `{ "foo": [ "all", "cows", "grass", "eat" ] }`, 135 | }, 136 | { 137 | `{ "foo": "bar" }`, 138 | `[ { "op": "add", "path": "/child", "value": { "grandchild": { } } } ]`, 139 | `{ "foo": "bar", "child": { "grandchild": { } } }`, 140 | }, 141 | { 142 | `{ "foo": ["bar"] }`, 143 | `[ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } ]`, 144 | `{ "foo": ["bar", ["abc", "def"]] }`, 145 | }, 146 | { 147 | `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`, 148 | `[ { "op": "remove", "path": "/qux/bar" } ]`, 149 | `{ "foo": "bar", "qux": { "baz": 1 } }`, 150 | }, 151 | { 152 | `{ "foo": "bar" }`, 153 | `[ { "op": "add", "path": "/baz", "value": null } ]`, 154 | `{ "baz": null, "foo": "bar" }`, 155 | }, 156 | { 157 | `{ "foo": ["bar"]}`, 158 | `[ { "op": "replace", "path": "/foo/0", "value": "baz"}]`, 159 | `{ "foo": ["baz"]}`, 160 | }, 161 | { 162 | `{ "foo": ["bar","baz"]}`, 163 | `[ { "op": "replace", "path": "/foo/0", "value": "bum"}]`, 164 | `{ "foo": ["bum","baz"]}`, 165 | }, 166 | { 167 | `{ "foo": ["bar","qux","baz"]}`, 168 | `[ { "op": "replace", "path": "/foo/1", "value": "bum"}]`, 169 | `{ "foo": ["bar", "bum","baz"]}`, 170 | }, 171 | { 172 | `[ {"foo": ["bar","qux","baz"]}]`, 173 | `[ { "op": "replace", "path": "/0/foo/0", "value": "bum"}]`, 174 | `[ {"foo": ["bum","qux","baz"]}]`, 175 | }, 176 | { 177 | `[ {"foo": ["bar","qux","baz"], "bar": ["qux","baz"]}]`, 178 | `[ { "op": "copy", "from": "/0/foo/0", "path": "/0/bar/0"}]`, 179 | `[ {"foo": ["bar","qux","baz"], "bar": ["bar", "baz"]}]`, 180 | }, 181 | { 182 | `[ {"foo": ["bar","qux","baz"], "bar": ["qux","baz"]}]`, 183 | `[ { "op": "copy", "from": "/0/foo/0", "path": "/0/bar"}]`, 184 | `[ {"foo": ["bar","qux","baz"], "bar": ["bar", "qux", "baz"]}]`, 185 | }, 186 | { 187 | `[ { "foo": {"bar": ["qux","baz"]}, "baz": {"qux": "bum"}}]`, 188 | `[ { "op": "copy", "from": "/0/foo/bar", "path": "/0/baz/bar"}]`, 189 | `[ { "baz": {"bar": ["qux","baz"], "qux":"bum"}, "foo": {"bar": ["qux","baz"]}}]`, 190 | }, 191 | { 192 | `{ "foo": ["bar"]}`, 193 | `[{"op": "copy", "path": "/foo/0", "from": "/foo"}]`, 194 | `{ "foo": [["bar"], "bar"]}`, 195 | }, 196 | { 197 | `{ "foo": ["bar","qux","baz"]}`, 198 | `[ { "op": "remove", "path": "/foo/-2"}]`, 199 | `{ "foo": ["bar", "baz"]}`, 200 | }, 201 | { 202 | `{ "foo": []}`, 203 | `[ { "op": "add", "path": "/foo/-1", "value": "qux"}]`, 204 | `{ "foo": ["qux"]}`, 205 | }, 206 | { 207 | `{ "bar": [{"baz": null}]}`, 208 | `[ { "op": "replace", "path": "/bar/0/baz", "value": 1 } ]`, 209 | `{ "bar": [{"baz": 1}]}`, 210 | }, 211 | { 212 | `{ "bar": [{"baz": 1}]}`, 213 | `[ { "op": "replace", "path": "/bar/0/baz", "value": null } ]`, 214 | `{ "bar": [{"baz": null}]}`, 215 | }, 216 | { 217 | `{ "bar": [null]}`, 218 | `[ { "op": "replace", "path": "/bar/0", "value": 1 } ]`, 219 | `{ "bar": [1]}`, 220 | }, 221 | { 222 | `{ "bar": [1]}`, 223 | `[ { "op": "replace", "path": "/bar/0", "value": null } ]`, 224 | `{ "bar": [null]}`, 225 | }, 226 | { 227 | fmt.Sprintf(`{ "foo": ["A", %q] }`, repeatedA(48)), 228 | // The wrapping quotes around 'A's are included in the copy 229 | // size, so each copy operation increases the size by 50 bytes. 230 | `[ { "op": "copy", "path": "/foo/-", "from": "/foo/1" }, 231 | { "op": "copy", "path": "/foo/-", "from": "/foo/1" }]`, 232 | fmt.Sprintf(`{ "foo": ["A", %q, %q, %q] }`, repeatedA(48), repeatedA(48), repeatedA(48)), 233 | }, 234 | { 235 | `{ 236 | "id": "00000000-0000-0000-0000-000000000000", 237 | "parentID": "00000000-0000-0000-0000-000000000000" 238 | }`, 239 | `[ 240 | { 241 | "op": "test", 242 | "path": "", 243 | "value": { 244 | "id": "00000000-0000-0000-0000-000000000000", 245 | "parentID": "00000000-0000-0000-0000-000000000000" 246 | } 247 | }, 248 | { 249 | "op": "replace", 250 | "path": "", 251 | "value": { 252 | "id": "759981e8-ec68-4639-a83e-513225914ecb", 253 | "originalID": "bar", 254 | "parentID": "00000000-0000-0000-0000-000000000000" 255 | } 256 | } 257 | ]`, 258 | `{ 259 | "id" : "759981e8-ec68-4639-a83e-513225914ecb", 260 | "originalID" : "bar", 261 | "parentID" : "00000000-0000-0000-0000-000000000000" 262 | }`, 263 | }, 264 | } 265 | 266 | type BadCase struct { 267 | doc, patch string 268 | } 269 | 270 | var MutationTestCases = []BadCase{ 271 | { 272 | `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`, 273 | `[ { "op": "remove", "path": "/qux/bar" } ]`, 274 | }, 275 | { 276 | `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`, 277 | `[ { "op": "replace", "path": "/qux/baz", "value": null } ]`, 278 | }, 279 | } 280 | 281 | var BadCases = []BadCase{ 282 | { 283 | `{ "foo": "bar" }`, 284 | `[ { "op": "add", "path": "/baz/bat", "value": "qux" } ]`, 285 | }, 286 | { 287 | `{ "a": { "b": { "d": 1 } } }`, 288 | `[ { "op": "remove", "path": "/a/b/c" } ]`, 289 | }, 290 | { 291 | `{ "a": { "b": { "d": 1 } } }`, 292 | `[ { "op": "move", "from": "/a/b/c", "path": "/a/b/e" } ]`, 293 | }, 294 | { 295 | `{ "a": { "b": [1] } }`, 296 | `[ { "op": "remove", "path": "/a/b/1" } ]`, 297 | }, 298 | { 299 | `{ "a": { "b": [1] } }`, 300 | `[ { "op": "move", "from": "/a/b/1", "path": "/a/b/2" } ]`, 301 | }, 302 | { 303 | `{ "foo": "bar" }`, 304 | `[ { "op": "add", "pathz": "/baz", "value": "qux" } ]`, 305 | }, 306 | { 307 | `{ "foo": "bar" }`, 308 | `[ { "op": "add", "path": "", "value": "qux" } ]`, 309 | }, 310 | { 311 | `{ "foo": ["bar","baz"]}`, 312 | `[ { "op": "replace", "path": "/foo/2", "value": "bum"}]`, 313 | }, 314 | { 315 | `{ "foo": ["bar","baz"]}`, 316 | `[ { "op": "add", "path": "/foo/-4", "value": "bum"}]`, 317 | }, 318 | { 319 | `{ "name":{ "foo": "bat", "qux": "bum"}}`, 320 | `[ { "op": "replace", "path": "/foo/bar", "value":"baz"}]`, 321 | }, 322 | { 323 | `{ "foo": ["bar"]}`, 324 | `[ {"op": "add", "path": "/foo/2", "value": "bum"}]`, 325 | }, 326 | { 327 | `{ "foo": []}`, 328 | `[ {"op": "remove", "path": "/foo/-"}]`, 329 | }, 330 | { 331 | `{ "foo": []}`, 332 | `[ {"op": "remove", "path": "/foo/-1"}]`, 333 | }, 334 | { 335 | `{ "foo": ["bar"]}`, 336 | `[ {"op": "remove", "path": "/foo/-2"}]`, 337 | }, 338 | { 339 | `{}`, 340 | `[ {"op":null,"path":""} ]`, 341 | }, 342 | { 343 | `{}`, 344 | `[ {"op":"add","path":null} ]`, 345 | }, 346 | { 347 | `{}`, 348 | `[ { "op": "copy", "from": null }]`, 349 | }, 350 | { 351 | `{ "foo": ["bar"]}`, 352 | `[{"op": "copy", "path": "/foo/6666666666", "from": "/"}]`, 353 | }, 354 | // Can't copy into an index greater than the size of the array 355 | { 356 | `{ "foo": ["bar"]}`, 357 | `[{"op": "copy", "path": "/foo/2", "from": "/foo/0"}]`, 358 | }, 359 | // Accumulated copy size cannot exceed AccumulatedCopySizeLimit. 360 | { 361 | fmt.Sprintf(`{ "foo": ["A", %q] }`, repeatedA(49)), 362 | // The wrapping quotes around 'A's are included in the copy 363 | // size, so each copy operation increases the size by 51 bytes. 364 | `[ { "op": "copy", "path": "/foo/-", "from": "/foo/1" }, 365 | { "op": "copy", "path": "/foo/-", "from": "/foo/1" }]`, 366 | }, 367 | // Can't move into an index greater than or equal to the size of the array 368 | { 369 | `{ "foo": [ "all", "grass", "cows", "eat" ] }`, 370 | `[ { "op": "move", "from": "/foo/1", "path": "/foo/4" } ]`, 371 | }, 372 | } 373 | 374 | // This is not thread safe, so we cannot run patch tests in parallel. 375 | func configureGlobals(accumulatedCopySizeLimit int64) func() { 376 | oldAccumulatedCopySizeLimit := AccumulatedCopySizeLimit 377 | AccumulatedCopySizeLimit = accumulatedCopySizeLimit 378 | return func() { 379 | AccumulatedCopySizeLimit = oldAccumulatedCopySizeLimit 380 | } 381 | } 382 | 383 | func TestAllCases(t *testing.T) { 384 | defer configureGlobals(int64(100))() 385 | for _, c := range Cases { 386 | out, err := applyPatch(c.doc, c.patch) 387 | 388 | if err != nil { 389 | t.Errorf("Unable to apply patch: %s", err) 390 | } 391 | 392 | if !compareJSON(out, c.result) { 393 | t.Errorf("Patch did not apply. Expected:\n%s\n\nActual:\n%s", 394 | reformatJSON(c.result), reformatJSON(out)) 395 | } 396 | } 397 | 398 | for _, c := range MutationTestCases { 399 | out, err := applyPatch(c.doc, c.patch) 400 | 401 | if err != nil { 402 | t.Errorf("Unable to apply patch: %s", err) 403 | } 404 | 405 | if compareJSON(out, c.doc) { 406 | t.Errorf("Patch did not apply. Original:\n%s\n\nPatched:\n%s", 407 | reformatJSON(c.doc), reformatJSON(out)) 408 | } 409 | } 410 | 411 | for _, c := range BadCases { 412 | _, err := applyPatch(c.doc, c.patch) 413 | 414 | if err == nil { 415 | t.Errorf("Patch %q should have failed to apply but it did not", c.patch) 416 | } 417 | } 418 | } 419 | 420 | type TestCase struct { 421 | doc, patch string 422 | result bool 423 | failedPath string 424 | } 425 | 426 | var TestCases = []TestCase{ 427 | { 428 | `{ 429 | "baz": "qux", 430 | "foo": [ "a", 2, "c" ] 431 | }`, 432 | `[ 433 | { "op": "test", "path": "/baz", "value": "qux" }, 434 | { "op": "test", "path": "/foo/1", "value": 2 } 435 | ]`, 436 | true, 437 | "", 438 | }, 439 | { 440 | `{ "baz": "qux" }`, 441 | `[ { "op": "test", "path": "/baz", "value": "bar" } ]`, 442 | false, 443 | "/baz", 444 | }, 445 | { 446 | `{ 447 | "baz": "qux", 448 | "foo": ["a", 2, "c"] 449 | }`, 450 | `[ 451 | { "op": "test", "path": "/baz", "value": "qux" }, 452 | { "op": "test", "path": "/foo/1", "value": "c" } 453 | ]`, 454 | false, 455 | "/foo/1", 456 | }, 457 | { 458 | `{ "baz": "qux" }`, 459 | `[ { "op": "test", "path": "/foo", "value": 42 } ]`, 460 | false, 461 | "/foo", 462 | }, 463 | { 464 | `{ "baz": "qux" }`, 465 | `[ { "op": "test", "path": "/foo", "value": null } ]`, 466 | true, 467 | "", 468 | }, 469 | { 470 | `{ "foo": null }`, 471 | `[ { "op": "test", "path": "/foo", "value": null } ]`, 472 | true, 473 | "", 474 | }, 475 | { 476 | `{ "foo": {} }`, 477 | `[ { "op": "test", "path": "/foo", "value": null } ]`, 478 | false, 479 | "/foo", 480 | }, 481 | { 482 | `{ "foo": [] }`, 483 | `[ { "op": "test", "path": "/foo", "value": null } ]`, 484 | false, 485 | "/foo", 486 | }, 487 | { 488 | `{ "baz/foo": "qux" }`, 489 | `[ { "op": "test", "path": "/baz~1foo", "value": "qux"} ]`, 490 | true, 491 | "", 492 | }, 493 | { 494 | `{ "foo": [] }`, 495 | `[ { "op": "test", "path": "/foo"} ]`, 496 | false, 497 | "/foo", 498 | }, 499 | { 500 | `{ "baz": [] }`, 501 | `[ { "op": "test", "path": "/foo"} ]`, 502 | true, 503 | "/foo", 504 | }, 505 | } 506 | 507 | func TestAllTest(t *testing.T) { 508 | for _, c := range TestCases { 509 | _, err := applyPatch(c.doc, c.patch) 510 | 511 | if c.result && err != nil { 512 | t.Errorf("Testing failed when it should have passed: %s", err) 513 | } else if !c.result && err == nil { 514 | t.Errorf("Testing passed when it should have faild: %s", err) 515 | } else if !c.result { 516 | expected := fmt.Sprintf("testing value %s failed: test failed", c.failedPath) 517 | if err.Error() != expected { 518 | t.Errorf("Testing failed as expected but invalid message: expected [%s], got [%s]", expected, err) 519 | } 520 | } 521 | } 522 | } 523 | 524 | func TestAdd(t *testing.T) { 525 | testCases := []struct { 526 | name string 527 | key string 528 | val lazyNode 529 | arr partialArray 530 | rejectNegativeIndicies bool 531 | err string 532 | }{ 533 | { 534 | name: "should work", 535 | key: "0", 536 | val: lazyNode{}, 537 | arr: partialArray{}, 538 | }, 539 | { 540 | name: "index too large", 541 | key: "1", 542 | val: lazyNode{}, 543 | arr: partialArray{}, 544 | err: "Unable to access invalid index: 1: invalid index referenced", 545 | }, 546 | { 547 | name: "negative should work", 548 | key: "-1", 549 | val: lazyNode{}, 550 | arr: partialArray{}, 551 | }, 552 | { 553 | name: "negative too small", 554 | key: "-2", 555 | val: lazyNode{}, 556 | arr: partialArray{}, 557 | err: "Unable to access invalid index: -2: invalid index referenced", 558 | }, 559 | { 560 | name: "negative but negative disabled", 561 | key: "-1", 562 | val: lazyNode{}, 563 | arr: partialArray{}, 564 | err: "Unable to access invalid index: -1: invalid index referenced", 565 | 566 | rejectNegativeIndicies: true, 567 | }, 568 | } 569 | for _, tc := range testCases { 570 | t.Run(tc.name, func(t *testing.T) { 571 | SupportNegativeIndices = !tc.rejectNegativeIndicies 572 | key := tc.key 573 | arr := &tc.arr 574 | val := &tc.val 575 | err := arr.add(key, val) 576 | if err == nil && tc.err != "" { 577 | t.Errorf("Expected error but got none! %v", tc.err) 578 | } else if err != nil && tc.err == "" { 579 | t.Errorf("Did not expect error but go: %v", err) 580 | } else if err != nil && err.Error() != tc.err { 581 | t.Errorf("Expected error %v but got error %v", tc.err, err) 582 | } 583 | }) 584 | } 585 | } 586 | 587 | type EqualityCase struct { 588 | name string 589 | a, b string 590 | equal bool 591 | } 592 | 593 | var EqualityCases = []EqualityCase{ 594 | { 595 | "ExtraKeyFalse", 596 | `{"foo": "bar"}`, 597 | `{"foo": "bar", "baz": "qux"}`, 598 | false, 599 | }, 600 | { 601 | "StripWhitespaceTrue", 602 | `{ 603 | "foo": "bar", 604 | "baz": "qux" 605 | }`, 606 | `{"foo": "bar", "baz": "qux"}`, 607 | true, 608 | }, 609 | { 610 | "KeysOutOfOrderTrue", 611 | `{ 612 | "baz": "qux", 613 | "foo": "bar" 614 | }`, 615 | `{"foo": "bar", "baz": "qux"}`, 616 | true, 617 | }, 618 | { 619 | "ComparingNullFalse", 620 | `{"foo": null}`, 621 | `{"foo": "bar"}`, 622 | false, 623 | }, 624 | { 625 | "ComparingNullTrue", 626 | `{"foo": null}`, 627 | `{"foo": null}`, 628 | true, 629 | }, 630 | { 631 | "ArrayOutOfOrderFalse", 632 | `["foo", "bar", "baz"]`, 633 | `["bar", "baz", "foo"]`, 634 | false, 635 | }, 636 | { 637 | "ArrayTrue", 638 | `["foo", "bar", "baz"]`, 639 | `["foo", "bar", "baz"]`, 640 | true, 641 | }, 642 | { 643 | "NonStringTypesTrue", 644 | `{"int": 6, "bool": true, "float": 7.0, "string": "the_string", "null": null}`, 645 | `{"int": 6, "bool": true, "float": 7.0, "string": "the_string", "null": null}`, 646 | true, 647 | }, 648 | { 649 | "NestedNullFalse", 650 | `{"foo": ["an", "array"], "bar": {"an": "object"}}`, 651 | `{"foo": null, "bar": null}`, 652 | false, 653 | }, 654 | { 655 | "NullCompareStringFalse", 656 | `"foo"`, 657 | `null`, 658 | false, 659 | }, 660 | { 661 | "NullCompareIntFalse", 662 | `6`, 663 | `null`, 664 | false, 665 | }, 666 | { 667 | "NullCompareFloatFalse", 668 | `6.01`, 669 | `null`, 670 | false, 671 | }, 672 | { 673 | "NullCompareBoolFalse", 674 | `false`, 675 | `null`, 676 | false, 677 | }, 678 | } 679 | 680 | func TestEquality(t *testing.T) { 681 | for _, tc := range EqualityCases { 682 | t.Run(tc.name, func(t *testing.T) { 683 | got := Equal([]byte(tc.a), []byte(tc.b)) 684 | if got != tc.equal { 685 | t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.a, tc.b, tc.equal, got) 686 | } 687 | 688 | got = Equal([]byte(tc.b), []byte(tc.a)) 689 | if got != tc.equal { 690 | t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.b, tc.a, tc.equal, got) 691 | } 692 | }) 693 | } 694 | } 695 | -------------------------------------------------------------------------------- /v5/bench_test.go: -------------------------------------------------------------------------------- 1 | package jsonpatch 2 | 3 | import "testing" 4 | 5 | func BenchmarkMergePatch(b *testing.B) { 6 | original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) 7 | target := []byte(`{"name": "Jane", "age": 24}`) 8 | alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`) 9 | 10 | patch, err := CreateMergePatch(original, target) 11 | if err != nil { 12 | panic(err) 13 | } 14 | 15 | for n := 0; n < b.N; n++ { 16 | MergePatch(alternative, patch) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /v5/cmd/json-patch/file_flag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Borrowed from Concourse: https://github.com/concourse/atc/blob/master/atccmd/file_flag.go 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | // FileFlag is a flag for passing a path to a file on disk. The file is 12 | // expected to be a file, not a directory, that actually exists. 13 | type FileFlag string 14 | 15 | // UnmarshalFlag implements go-flag's Unmarshaler interface 16 | func (f *FileFlag) UnmarshalFlag(value string) error { 17 | stat, err := os.Stat(value) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | if stat.IsDir() { 23 | return fmt.Errorf("path '%s' is a directory, not a file", value) 24 | } 25 | 26 | abs, err := filepath.Abs(value) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | *f = FileFlag(abs) 32 | 33 | return nil 34 | } 35 | 36 | // Path is the path to the file 37 | func (f FileFlag) Path() string { 38 | return string(f) 39 | } 40 | -------------------------------------------------------------------------------- /v5/cmd/json-patch/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | 9 | jsonpatch "github.com/evanphx/json-patch/v5" 10 | flags "github.com/jessevdk/go-flags" 11 | ) 12 | 13 | type opts struct { 14 | PatchFilePaths []FileFlag `long:"patch-file" short:"p" value-name:"PATH" description:"Path to file with one or more operations"` 15 | } 16 | 17 | func main() { 18 | var o opts 19 | _, err := flags.Parse(&o) 20 | if err != nil { 21 | log.Fatalf("error: %s\n", err) 22 | } 23 | 24 | patches := make([]jsonpatch.Patch, len(o.PatchFilePaths)) 25 | 26 | for i, patchFilePath := range o.PatchFilePaths { 27 | var bs []byte 28 | bs, err = ioutil.ReadFile(patchFilePath.Path()) 29 | if err != nil { 30 | log.Fatalf("error reading patch file: %s", err) 31 | } 32 | 33 | var patch jsonpatch.Patch 34 | patch, err = jsonpatch.DecodePatch(bs) 35 | if err != nil { 36 | log.Fatalf("error decoding patch file: %s", err) 37 | } 38 | 39 | patches[i] = patch 40 | } 41 | 42 | doc, err := ioutil.ReadAll(os.Stdin) 43 | if err != nil { 44 | log.Fatalf("error reading from stdin: %s", err) 45 | } 46 | 47 | mdoc := doc 48 | for _, patch := range patches { 49 | mdoc, err = patch.Apply(mdoc) 50 | if err != nil { 51 | log.Fatalf("error applying patch: %s", err) 52 | } 53 | } 54 | 55 | fmt.Printf("%s", mdoc) 56 | } 57 | -------------------------------------------------------------------------------- /v5/errors.go: -------------------------------------------------------------------------------- 1 | package jsonpatch 2 | 3 | import "fmt" 4 | 5 | // AccumulatedCopySizeError is an error type returned when the accumulated size 6 | // increase caused by copy operations in a patch operation has exceeded the 7 | // limit. 8 | type AccumulatedCopySizeError struct { 9 | limit int64 10 | accumulated int64 11 | } 12 | 13 | // NewAccumulatedCopySizeError returns an AccumulatedCopySizeError. 14 | func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError { 15 | return &AccumulatedCopySizeError{limit: l, accumulated: a} 16 | } 17 | 18 | // Error implements the error interface. 19 | func (a *AccumulatedCopySizeError) Error() string { 20 | return fmt.Sprintf("Unable to complete the copy, the accumulated size increase of copy is %d, exceeding the limit %d", a.accumulated, a.limit) 21 | } 22 | 23 | // ArraySizeError is an error type returned when the array size has exceeded 24 | // the limit. 25 | type ArraySizeError struct { 26 | limit int 27 | size int 28 | } 29 | 30 | // NewArraySizeError returns an ArraySizeError. 31 | func NewArraySizeError(l, s int) *ArraySizeError { 32 | return &ArraySizeError{limit: l, size: s} 33 | } 34 | 35 | // Error implements the error interface. 36 | func (a *ArraySizeError) Error() string { 37 | return fmt.Sprintf("Unable to create array of size %d, limit is %d", a.size, a.limit) 38 | } 39 | -------------------------------------------------------------------------------- /v5/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/evanphx/json-patch/v5 2 | 3 | go 1.18 4 | 5 | require github.com/jessevdk/go-flags v1.6.1 6 | 7 | require golang.org/x/sys v0.21.0 // indirect 8 | -------------------------------------------------------------------------------- /v5/go.sum: -------------------------------------------------------------------------------- 1 | github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= 2 | github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= 3 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 4 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | -------------------------------------------------------------------------------- /v5/internal/json/example_marshaling_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | type Animal int 15 | 16 | const ( 17 | Unknown Animal = iota 18 | Gopher 19 | Zebra 20 | ) 21 | 22 | func (a *Animal) UnmarshalJSON(b []byte) error { 23 | var s string 24 | if err := json.Unmarshal(b, &s); err != nil { 25 | return err 26 | } 27 | switch strings.ToLower(s) { 28 | default: 29 | *a = Unknown 30 | case "gopher": 31 | *a = Gopher 32 | case "zebra": 33 | *a = Zebra 34 | } 35 | 36 | return nil 37 | } 38 | 39 | func (a Animal) MarshalJSON() ([]byte, error) { 40 | var s string 41 | switch a { 42 | default: 43 | s = "unknown" 44 | case Gopher: 45 | s = "gopher" 46 | case Zebra: 47 | s = "zebra" 48 | } 49 | 50 | return json.Marshal(s) 51 | } 52 | 53 | func Example_customMarshalJSON() { 54 | blob := `["gopher","armadillo","zebra","unknown","gopher","bee","gopher","zebra"]` 55 | var zoo []Animal 56 | if err := json.Unmarshal([]byte(blob), &zoo); err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | census := make(map[Animal]int) 61 | for _, animal := range zoo { 62 | census[animal] += 1 63 | } 64 | 65 | fmt.Printf("Zoo Census:\n* Gophers: %d\n* Zebras: %d\n* Unknown: %d\n", 66 | census[Gopher], census[Zebra], census[Unknown]) 67 | 68 | // Output: 69 | // Zoo Census: 70 | // * Gophers: 3 71 | // * Zebras: 2 72 | // * Unknown: 3 73 | } 74 | -------------------------------------------------------------------------------- /v5/internal/json/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "log" 13 | "os" 14 | "strings" 15 | ) 16 | 17 | func ExampleMarshal() { 18 | type ColorGroup struct { 19 | ID int 20 | Name string 21 | Colors []string 22 | } 23 | group := ColorGroup{ 24 | ID: 1, 25 | Name: "Reds", 26 | Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, 27 | } 28 | b, err := json.Marshal(group) 29 | if err != nil { 30 | fmt.Println("error:", err) 31 | } 32 | os.Stdout.Write(b) 33 | // Output: 34 | // {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} 35 | } 36 | 37 | func ExampleUnmarshal() { 38 | var jsonBlob = []byte(`[ 39 | {"Name": "Platypus", "Order": "Monotremata"}, 40 | {"Name": "Quoll", "Order": "Dasyuromorphia"} 41 | ]`) 42 | type Animal struct { 43 | Name string 44 | Order string 45 | } 46 | var animals []Animal 47 | err := json.Unmarshal(jsonBlob, &animals) 48 | if err != nil { 49 | fmt.Println("error:", err) 50 | } 51 | fmt.Printf("%+v", animals) 52 | // Output: 53 | // [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] 54 | } 55 | 56 | // This example uses a Decoder to decode a stream of distinct JSON values. 57 | func ExampleDecoder() { 58 | const jsonStream = ` 59 | {"Name": "Ed", "Text": "Knock knock."} 60 | {"Name": "Sam", "Text": "Who's there?"} 61 | {"Name": "Ed", "Text": "Go fmt."} 62 | {"Name": "Sam", "Text": "Go fmt who?"} 63 | {"Name": "Ed", "Text": "Go fmt yourself!"} 64 | ` 65 | type Message struct { 66 | Name, Text string 67 | } 68 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 69 | for { 70 | var m Message 71 | if err := dec.Decode(&m); err == io.EOF { 72 | break 73 | } else if err != nil { 74 | log.Fatal(err) 75 | } 76 | fmt.Printf("%s: %s\n", m.Name, m.Text) 77 | } 78 | // Output: 79 | // Ed: Knock knock. 80 | // Sam: Who's there? 81 | // Ed: Go fmt. 82 | // Sam: Go fmt who? 83 | // Ed: Go fmt yourself! 84 | } 85 | 86 | // This example uses a Decoder to decode a stream of distinct JSON values. 87 | func ExampleDecoder_Token() { 88 | const jsonStream = ` 89 | {"Message": "Hello", "Array": [1, 2, 3], "Null": null, "Number": 1.234} 90 | ` 91 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 92 | for { 93 | t, err := dec.Token() 94 | if err == io.EOF { 95 | break 96 | } 97 | if err != nil { 98 | log.Fatal(err) 99 | } 100 | fmt.Printf("%T: %v", t, t) 101 | if dec.More() { 102 | fmt.Printf(" (more)") 103 | } 104 | fmt.Printf("\n") 105 | } 106 | // Output: 107 | // json.Delim: { (more) 108 | // string: Message (more) 109 | // string: Hello (more) 110 | // string: Array (more) 111 | // json.Delim: [ (more) 112 | // float64: 1 (more) 113 | // float64: 2 (more) 114 | // float64: 3 115 | // json.Delim: ] (more) 116 | // string: Null (more) 117 | // : (more) 118 | // string: Number (more) 119 | // float64: 1.234 120 | // json.Delim: } 121 | } 122 | 123 | // This example uses a Decoder to decode a streaming array of JSON objects. 124 | func ExampleDecoder_Decode_stream() { 125 | const jsonStream = ` 126 | [ 127 | {"Name": "Ed", "Text": "Knock knock."}, 128 | {"Name": "Sam", "Text": "Who's there?"}, 129 | {"Name": "Ed", "Text": "Go fmt."}, 130 | {"Name": "Sam", "Text": "Go fmt who?"}, 131 | {"Name": "Ed", "Text": "Go fmt yourself!"} 132 | ] 133 | ` 134 | type Message struct { 135 | Name, Text string 136 | } 137 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 138 | 139 | // read open bracket 140 | t, err := dec.Token() 141 | if err != nil { 142 | log.Fatal(err) 143 | } 144 | fmt.Printf("%T: %v\n", t, t) 145 | 146 | // while the array contains values 147 | for dec.More() { 148 | var m Message 149 | // decode an array value (Message) 150 | err := dec.Decode(&m) 151 | if err != nil { 152 | log.Fatal(err) 153 | } 154 | 155 | fmt.Printf("%v: %v\n", m.Name, m.Text) 156 | } 157 | 158 | // read closing bracket 159 | t, err = dec.Token() 160 | if err != nil { 161 | log.Fatal(err) 162 | } 163 | fmt.Printf("%T: %v\n", t, t) 164 | 165 | // Output: 166 | // json.Delim: [ 167 | // Ed: Knock knock. 168 | // Sam: Who's there? 169 | // Ed: Go fmt. 170 | // Sam: Go fmt who? 171 | // Ed: Go fmt yourself! 172 | // json.Delim: ] 173 | } 174 | 175 | // This example uses RawMessage to delay parsing part of a JSON message. 176 | func ExampleRawMessage_unmarshal() { 177 | type Color struct { 178 | Space string 179 | Point json.RawMessage // delay parsing until we know the color space 180 | } 181 | type RGB struct { 182 | R uint8 183 | G uint8 184 | B uint8 185 | } 186 | type YCbCr struct { 187 | Y uint8 188 | Cb int8 189 | Cr int8 190 | } 191 | 192 | var j = []byte(`[ 193 | {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}}, 194 | {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}} 195 | ]`) 196 | var colors []Color 197 | err := json.Unmarshal(j, &colors) 198 | if err != nil { 199 | log.Fatalln("error:", err) 200 | } 201 | 202 | for _, c := range colors { 203 | var dst any 204 | switch c.Space { 205 | case "RGB": 206 | dst = new(RGB) 207 | case "YCbCr": 208 | dst = new(YCbCr) 209 | } 210 | err := json.Unmarshal(c.Point, dst) 211 | if err != nil { 212 | log.Fatalln("error:", err) 213 | } 214 | fmt.Println(c.Space, dst) 215 | } 216 | // Output: 217 | // YCbCr &{255 0 -10} 218 | // RGB &{98 218 255} 219 | } 220 | 221 | // This example uses RawMessage to use a precomputed JSON during marshal. 222 | func ExampleRawMessage_marshal() { 223 | h := json.RawMessage(`{"precomputed": true}`) 224 | 225 | c := struct { 226 | Header *json.RawMessage `json:"header"` 227 | Body string `json:"body"` 228 | }{Header: &h, Body: "Hello Gophers!"} 229 | 230 | b, err := json.MarshalIndent(&c, "", "\t") 231 | if err != nil { 232 | fmt.Println("error:", err) 233 | } 234 | os.Stdout.Write(b) 235 | 236 | // Output: 237 | // { 238 | // "header": { 239 | // "precomputed": true 240 | // }, 241 | // "body": "Hello Gophers!" 242 | // } 243 | } 244 | 245 | func ExampleIndent() { 246 | type Road struct { 247 | Name string 248 | Number int 249 | } 250 | roads := []Road{ 251 | {"Diamond Fork", 29}, 252 | {"Sheep Creek", 51}, 253 | } 254 | 255 | b, err := json.Marshal(roads) 256 | if err != nil { 257 | log.Fatal(err) 258 | } 259 | 260 | var out bytes.Buffer 261 | json.Indent(&out, b, "=", "\t") 262 | out.WriteTo(os.Stdout) 263 | // Output: 264 | // [ 265 | // = { 266 | // = "Name": "Diamond Fork", 267 | // = "Number": 29 268 | // = }, 269 | // = { 270 | // = "Name": "Sheep Creek", 271 | // = "Number": 51 272 | // = } 273 | // =] 274 | } 275 | 276 | func ExampleMarshalIndent() { 277 | data := map[string]int{ 278 | "a": 1, 279 | "b": 2, 280 | } 281 | 282 | b, err := json.MarshalIndent(data, "", "") 283 | if err != nil { 284 | log.Fatal(err) 285 | } 286 | 287 | fmt.Println(string(b)) 288 | // Output: 289 | // { 290 | // "a": 1, 291 | // "b": 2 292 | // } 293 | } 294 | 295 | func ExampleValid() { 296 | goodJSON := `{"example": 1}` 297 | badJSON := `{"example":2:]}}` 298 | 299 | fmt.Println(json.Valid([]byte(goodJSON)), json.Valid([]byte(badJSON))) 300 | // Output: 301 | // true false 302 | } 303 | 304 | func ExampleHTMLEscape() { 305 | var out bytes.Buffer 306 | json.HTMLEscape(&out, []byte(`{"Name":"HTML content"}`)) 307 | out.WriteTo(os.Stdout) 308 | // Output: 309 | //{"Name":"\u003cb\u003eHTML content\u003c/b\u003e"} 310 | } 311 | -------------------------------------------------------------------------------- /v5/internal/json/example_text_marshaling_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | type Size int 15 | 16 | const ( 17 | Unrecognized Size = iota 18 | Small 19 | Large 20 | ) 21 | 22 | func (s *Size) UnmarshalText(text []byte) error { 23 | switch strings.ToLower(string(text)) { 24 | default: 25 | *s = Unrecognized 26 | case "small": 27 | *s = Small 28 | case "large": 29 | *s = Large 30 | } 31 | return nil 32 | } 33 | 34 | func (s Size) MarshalText() ([]byte, error) { 35 | var name string 36 | switch s { 37 | default: 38 | name = "unrecognized" 39 | case Small: 40 | name = "small" 41 | case Large: 42 | name = "large" 43 | } 44 | return []byte(name), nil 45 | } 46 | 47 | func Example_textMarshalJSON() { 48 | blob := `["small","regular","large","unrecognized","small","normal","small","large"]` 49 | var inventory []Size 50 | if err := json.Unmarshal([]byte(blob), &inventory); err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | counts := make(map[Size]int) 55 | for _, size := range inventory { 56 | counts[size] += 1 57 | } 58 | 59 | fmt.Printf("Inventory Counts:\n* Small: %d\n* Large: %d\n* Unrecognized: %d\n", 60 | counts[Small], counts[Large], counts[Unrecognized]) 61 | 62 | // Output: 63 | // Inventory Counts: 64 | // * Small: 3 65 | // * Large: 2 66 | // * Unrecognized: 3 67 | } 68 | -------------------------------------------------------------------------------- /v5/internal/json/fold.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "unicode/utf8" 10 | ) 11 | 12 | const ( 13 | caseMask = ^byte(0x20) // Mask to ignore case in ASCII. 14 | kelvin = '\u212a' 15 | smallLongEss = '\u017f' 16 | ) 17 | 18 | // foldFunc returns one of four different case folding equivalence 19 | // functions, from most general (and slow) to fastest: 20 | // 21 | // 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 22 | // 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') 23 | // 3) asciiEqualFold, no special, but includes non-letters (including _) 24 | // 4) simpleLetterEqualFold, no specials, no non-letters. 25 | // 26 | // The letters S and K are special because they map to 3 runes, not just 2: 27 | // - S maps to s and to U+017F 'ſ' Latin small letter long s 28 | // - k maps to K and to U+212A 'K' Kelvin sign 29 | // 30 | // See https://play.golang.org/p/tTxjOc0OGo 31 | // 32 | // The returned function is specialized for matching against s and 33 | // should only be given s. It's not curried for performance reasons. 34 | func foldFunc(s []byte) func(s, t []byte) bool { 35 | nonLetter := false 36 | special := false // special letter 37 | for _, b := range s { 38 | if b >= utf8.RuneSelf { 39 | return bytes.EqualFold 40 | } 41 | upper := b & caseMask 42 | if upper < 'A' || upper > 'Z' { 43 | nonLetter = true 44 | } else if upper == 'K' || upper == 'S' { 45 | // See above for why these letters are special. 46 | special = true 47 | } 48 | } 49 | if special { 50 | return equalFoldRight 51 | } 52 | if nonLetter { 53 | return asciiEqualFold 54 | } 55 | return simpleLetterEqualFold 56 | } 57 | 58 | // equalFoldRight is a specialization of bytes.EqualFold when s is 59 | // known to be all ASCII (including punctuation), but contains an 's', 60 | // 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. 61 | // See comments on foldFunc. 62 | func equalFoldRight(s, t []byte) bool { 63 | for _, sb := range s { 64 | if len(t) == 0 { 65 | return false 66 | } 67 | tb := t[0] 68 | if tb < utf8.RuneSelf { 69 | if sb != tb { 70 | sbUpper := sb & caseMask 71 | if 'A' <= sbUpper && sbUpper <= 'Z' { 72 | if sbUpper != tb&caseMask { 73 | return false 74 | } 75 | } else { 76 | return false 77 | } 78 | } 79 | t = t[1:] 80 | continue 81 | } 82 | // sb is ASCII and t is not. t must be either kelvin 83 | // sign or long s; sb must be s, S, k, or K. 84 | tr, size := utf8.DecodeRune(t) 85 | switch sb { 86 | case 's', 'S': 87 | if tr != smallLongEss { 88 | return false 89 | } 90 | case 'k', 'K': 91 | if tr != kelvin { 92 | return false 93 | } 94 | default: 95 | return false 96 | } 97 | t = t[size:] 98 | 99 | } 100 | return len(t) == 0 101 | } 102 | 103 | // asciiEqualFold is a specialization of bytes.EqualFold for use when 104 | // s is all ASCII (but may contain non-letters) and contains no 105 | // special-folding letters. 106 | // See comments on foldFunc. 107 | func asciiEqualFold(s, t []byte) bool { 108 | if len(s) != len(t) { 109 | return false 110 | } 111 | for i, sb := range s { 112 | tb := t[i] 113 | if sb == tb { 114 | continue 115 | } 116 | if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { 117 | if sb&caseMask != tb&caseMask { 118 | return false 119 | } 120 | } else { 121 | return false 122 | } 123 | } 124 | return true 125 | } 126 | 127 | // simpleLetterEqualFold is a specialization of bytes.EqualFold for 128 | // use when s is all ASCII letters (no underscores, etc) and also 129 | // doesn't contain 'k', 'K', 's', or 'S'. 130 | // See comments on foldFunc. 131 | func simpleLetterEqualFold(s, t []byte) bool { 132 | if len(s) != len(t) { 133 | return false 134 | } 135 | for i, b := range s { 136 | if b&caseMask != t[i]&caseMask { 137 | return false 138 | } 139 | } 140 | return true 141 | } 142 | -------------------------------------------------------------------------------- /v5/internal/json/fold_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "strings" 10 | "testing" 11 | "unicode/utf8" 12 | ) 13 | 14 | var foldTests = []struct { 15 | fn func(s, t []byte) bool 16 | s, t string 17 | want bool 18 | }{ 19 | {equalFoldRight, "", "", true}, 20 | {equalFoldRight, "a", "a", true}, 21 | {equalFoldRight, "", "a", false}, 22 | {equalFoldRight, "a", "", false}, 23 | {equalFoldRight, "a", "A", true}, 24 | {equalFoldRight, "AB", "ab", true}, 25 | {equalFoldRight, "AB", "ac", false}, 26 | {equalFoldRight, "sbkKc", "ſbKKc", true}, 27 | {equalFoldRight, "SbKkc", "ſbKKc", true}, 28 | {equalFoldRight, "SbKkc", "ſbKK", false}, 29 | {equalFoldRight, "e", "é", false}, 30 | {equalFoldRight, "s", "S", true}, 31 | 32 | {simpleLetterEqualFold, "", "", true}, 33 | {simpleLetterEqualFold, "abc", "abc", true}, 34 | {simpleLetterEqualFold, "abc", "ABC", true}, 35 | {simpleLetterEqualFold, "abc", "ABCD", false}, 36 | {simpleLetterEqualFold, "abc", "xxx", false}, 37 | 38 | {asciiEqualFold, "a_B", "A_b", true}, 39 | {asciiEqualFold, "aa@", "aa`", false}, // verify 0x40 and 0x60 aren't case-equivalent 40 | } 41 | 42 | func TestFold(t *testing.T) { 43 | for i, tt := range foldTests { 44 | if got := tt.fn([]byte(tt.s), []byte(tt.t)); got != tt.want { 45 | t.Errorf("%d. %q, %q = %v; want %v", i, tt.s, tt.t, got, tt.want) 46 | } 47 | truth := strings.EqualFold(tt.s, tt.t) 48 | if truth != tt.want { 49 | t.Errorf("strings.EqualFold doesn't agree with case %d", i) 50 | } 51 | } 52 | } 53 | 54 | func TestFoldAgainstUnicode(t *testing.T) { 55 | var buf1, buf2 []byte 56 | var runes []rune 57 | for i := 0x20; i <= 0x7f; i++ { 58 | runes = append(runes, rune(i)) 59 | } 60 | runes = append(runes, kelvin, smallLongEss) 61 | 62 | funcs := []struct { 63 | name string 64 | fold func(s, t []byte) bool 65 | letter bool // must be ASCII letter 66 | simple bool // must be simple ASCII letter (not 'S' or 'K') 67 | }{ 68 | { 69 | name: "equalFoldRight", 70 | fold: equalFoldRight, 71 | }, 72 | { 73 | name: "asciiEqualFold", 74 | fold: asciiEqualFold, 75 | simple: true, 76 | }, 77 | { 78 | name: "simpleLetterEqualFold", 79 | fold: simpleLetterEqualFold, 80 | simple: true, 81 | letter: true, 82 | }, 83 | } 84 | 85 | for _, ff := range funcs { 86 | for _, r := range runes { 87 | if r >= utf8.RuneSelf { 88 | continue 89 | } 90 | if ff.letter && !isASCIILetter(byte(r)) { 91 | continue 92 | } 93 | if ff.simple && (r == 's' || r == 'S' || r == 'k' || r == 'K') { 94 | continue 95 | } 96 | for _, r2 := range runes { 97 | buf1 = append(utf8.AppendRune(append(buf1[:0], 'x'), r), 'x') 98 | buf2 = append(utf8.AppendRune(append(buf2[:0], 'x'), r2), 'x') 99 | want := bytes.EqualFold(buf1, buf2) 100 | if got := ff.fold(buf1, buf2); got != want { 101 | t.Errorf("%s(%q, %q) = %v; want %v", ff.name, buf1, buf2, got, want) 102 | } 103 | } 104 | } 105 | } 106 | } 107 | 108 | func isASCIILetter(b byte) bool { 109 | return ('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z') 110 | } 111 | -------------------------------------------------------------------------------- /v5/internal/json/fuzz.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build gofuzz 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | func Fuzz(data []byte) (score int) { 14 | for _, ctor := range []func() any{ 15 | func() any { return new(any) }, 16 | func() any { return new(map[string]any) }, 17 | func() any { return new([]any) }, 18 | } { 19 | v := ctor() 20 | err := Unmarshal(data, v) 21 | if err != nil { 22 | continue 23 | } 24 | score = 1 25 | 26 | m, err := Marshal(v) 27 | if err != nil { 28 | fmt.Printf("v=%#v\n", v) 29 | panic(err) 30 | } 31 | 32 | u := ctor() 33 | err = Unmarshal(m, u) 34 | if err != nil { 35 | fmt.Printf("v=%#v\n", v) 36 | fmt.Printf("m=%s\n", m) 37 | panic(err) 38 | } 39 | } 40 | 41 | return 42 | } 43 | -------------------------------------------------------------------------------- /v5/internal/json/fuzz_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "testing" 11 | ) 12 | 13 | func FuzzUnmarshalJSON(f *testing.F) { 14 | f.Add([]byte(`{ 15 | "object": { 16 | "slice": [ 17 | 1, 18 | 2.0, 19 | "3", 20 | [4], 21 | {5: {}} 22 | ] 23 | }, 24 | "slice": [[]], 25 | "string": ":)", 26 | "int": 1e5, 27 | "float": 3e-9" 28 | }`)) 29 | 30 | f.Fuzz(func(t *testing.T, b []byte) { 31 | for _, typ := range []func() interface{}{ 32 | func() interface{} { return new(interface{}) }, 33 | func() interface{} { return new(map[string]interface{}) }, 34 | func() interface{} { return new([]interface{}) }, 35 | } { 36 | i := typ() 37 | if err := Unmarshal(b, i); err != nil { 38 | return 39 | } 40 | 41 | encoded, err := Marshal(i) 42 | if err != nil { 43 | t.Fatalf("failed to marshal: %s", err) 44 | } 45 | 46 | if err := Unmarshal(encoded, i); err != nil { 47 | t.Fatalf("failed to roundtrip: %s", err) 48 | } 49 | } 50 | }) 51 | } 52 | 53 | func FuzzDecoderToken(f *testing.F) { 54 | f.Add([]byte(`{ 55 | "object": { 56 | "slice": [ 57 | 1, 58 | 2.0, 59 | "3", 60 | [4], 61 | {5: {}} 62 | ] 63 | }, 64 | "slice": [[]], 65 | "string": ":)", 66 | "int": 1e5, 67 | "float": 3e-9" 68 | }`)) 69 | 70 | f.Fuzz(func(t *testing.T, b []byte) { 71 | r := bytes.NewReader(b) 72 | d := NewDecoder(r) 73 | for { 74 | _, err := d.Token() 75 | if err != nil { 76 | if err == io.EOF { 77 | break 78 | } 79 | return 80 | } 81 | } 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /v5/internal/json/indent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | ) 10 | 11 | // Compact appends to dst the JSON-encoded src with 12 | // insignificant space characters elided. 13 | func Compact(dst *bytes.Buffer, src []byte) error { 14 | return compact(dst, src, false) 15 | } 16 | 17 | func compact(dst *bytes.Buffer, src []byte, escape bool) error { 18 | origLen := dst.Len() 19 | scan := newScanner() 20 | defer freeScanner(scan) 21 | start := 0 22 | for i, c := range src { 23 | if escape && (c == '<' || c == '>' || c == '&') { 24 | if start < i { 25 | dst.Write(src[start:i]) 26 | } 27 | dst.WriteString(`\u00`) 28 | dst.WriteByte(hex[c>>4]) 29 | dst.WriteByte(hex[c&0xF]) 30 | start = i + 1 31 | } 32 | // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). 33 | if escape && c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { 34 | if start < i { 35 | dst.Write(src[start:i]) 36 | } 37 | dst.WriteString(`\u202`) 38 | dst.WriteByte(hex[src[i+2]&0xF]) 39 | start = i + 3 40 | } 41 | v := scan.step(scan, c) 42 | if v >= scanSkipSpace { 43 | if v == scanError { 44 | break 45 | } 46 | if start < i { 47 | dst.Write(src[start:i]) 48 | } 49 | start = i + 1 50 | } 51 | } 52 | if scan.eof() == scanError { 53 | dst.Truncate(origLen) 54 | return scan.err 55 | } 56 | if start < len(src) { 57 | dst.Write(src[start:]) 58 | } 59 | return nil 60 | } 61 | 62 | func newline(dst *bytes.Buffer, prefix, indent string, depth int) { 63 | dst.WriteByte('\n') 64 | dst.WriteString(prefix) 65 | for i := 0; i < depth; i++ { 66 | dst.WriteString(indent) 67 | } 68 | } 69 | 70 | // Indent appends to dst an indented form of the JSON-encoded src. 71 | // Each element in a JSON object or array begins on a new, 72 | // indented line beginning with prefix followed by one or more 73 | // copies of indent according to the indentation nesting. 74 | // The data appended to dst does not begin with the prefix nor 75 | // any indentation, to make it easier to embed inside other formatted JSON data. 76 | // Although leading space characters (space, tab, carriage return, newline) 77 | // at the beginning of src are dropped, trailing space characters 78 | // at the end of src are preserved and copied to dst. 79 | // For example, if src has no trailing spaces, neither will dst; 80 | // if src ends in a trailing newline, so will dst. 81 | func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { 82 | origLen := dst.Len() 83 | scan := newScanner() 84 | defer freeScanner(scan) 85 | needIndent := false 86 | depth := 0 87 | for _, c := range src { 88 | scan.bytes++ 89 | v := scan.step(scan, c) 90 | if v == scanSkipSpace { 91 | continue 92 | } 93 | if v == scanError { 94 | break 95 | } 96 | if needIndent && v != scanEndObject && v != scanEndArray { 97 | needIndent = false 98 | depth++ 99 | newline(dst, prefix, indent, depth) 100 | } 101 | 102 | // Emit semantically uninteresting bytes 103 | // (in particular, punctuation in strings) unmodified. 104 | if v == scanContinue { 105 | dst.WriteByte(c) 106 | continue 107 | } 108 | 109 | // Add spacing around real punctuation. 110 | switch c { 111 | case '{', '[': 112 | // delay indent so that empty object and array are formatted as {} and []. 113 | needIndent = true 114 | dst.WriteByte(c) 115 | 116 | case ',': 117 | dst.WriteByte(c) 118 | newline(dst, prefix, indent, depth) 119 | 120 | case ':': 121 | dst.WriteByte(c) 122 | dst.WriteByte(' ') 123 | 124 | case '}', ']': 125 | if needIndent { 126 | // suppress indent in empty object/array 127 | needIndent = false 128 | } else { 129 | depth-- 130 | newline(dst, prefix, indent, depth) 131 | } 132 | dst.WriteByte(c) 133 | 134 | default: 135 | dst.WriteByte(c) 136 | } 137 | } 138 | if scan.eof() == scanError { 139 | dst.Truncate(origLen) 140 | return scan.err 141 | } 142 | return nil 143 | } 144 | -------------------------------------------------------------------------------- /v5/internal/json/number_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "regexp" 9 | "testing" 10 | ) 11 | 12 | func TestNumberIsValid(t *testing.T) { 13 | // From: https://stackoverflow.com/a/13340826 14 | var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 15 | 16 | validTests := []string{ 17 | "0", 18 | "-0", 19 | "1", 20 | "-1", 21 | "0.1", 22 | "-0.1", 23 | "1234", 24 | "-1234", 25 | "12.34", 26 | "-12.34", 27 | "12E0", 28 | "12E1", 29 | "12e34", 30 | "12E-0", 31 | "12e+1", 32 | "12e-34", 33 | "-12E0", 34 | "-12E1", 35 | "-12e34", 36 | "-12E-0", 37 | "-12e+1", 38 | "-12e-34", 39 | "1.2E0", 40 | "1.2E1", 41 | "1.2e34", 42 | "1.2E-0", 43 | "1.2e+1", 44 | "1.2e-34", 45 | "-1.2E0", 46 | "-1.2E1", 47 | "-1.2e34", 48 | "-1.2E-0", 49 | "-1.2e+1", 50 | "-1.2e-34", 51 | "0E0", 52 | "0E1", 53 | "0e34", 54 | "0E-0", 55 | "0e+1", 56 | "0e-34", 57 | "-0E0", 58 | "-0E1", 59 | "-0e34", 60 | "-0E-0", 61 | "-0e+1", 62 | "-0e-34", 63 | } 64 | 65 | for _, test := range validTests { 66 | if !isValidNumber(test) { 67 | t.Errorf("%s should be valid", test) 68 | } 69 | 70 | var f float64 71 | if err := Unmarshal([]byte(test), &f); err != nil { 72 | t.Errorf("%s should be valid but Unmarshal failed: %v", test, err) 73 | } 74 | 75 | if !jsonNumberRegexp.MatchString(test) { 76 | t.Errorf("%s should be valid but regexp does not match", test) 77 | } 78 | } 79 | 80 | invalidTests := []string{ 81 | "", 82 | "invalid", 83 | "1.0.1", 84 | "1..1", 85 | "-1-2", 86 | "012a42", 87 | "01.2", 88 | "012", 89 | "12E12.12", 90 | "1e2e3", 91 | "1e+-2", 92 | "1e--23", 93 | "1e", 94 | "e1", 95 | "1e+", 96 | "1ea", 97 | "1a", 98 | "1.a", 99 | "1.", 100 | "01", 101 | "1.e1", 102 | } 103 | 104 | for _, test := range invalidTests { 105 | if isValidNumber(test) { 106 | t.Errorf("%s should be invalid", test) 107 | } 108 | 109 | var f float64 110 | if err := Unmarshal([]byte(test), &f); err == nil { 111 | t.Errorf("%s should be invalid but unmarshal wrote %v", test, f) 112 | } 113 | 114 | if jsonNumberRegexp.MatchString(test) { 115 | t.Errorf("%s should be invalid but matches regexp", test) 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /v5/internal/json/scanner.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | // JSON value parser state machine. 8 | // Just about at the limit of what is reasonable to write by hand. 9 | // Some parts are a bit tedious, but overall it nicely factors out the 10 | // otherwise common code from the multiple scanning functions 11 | // in this package (Compact, Indent, checkValid, etc). 12 | // 13 | // This file starts with two simple examples using the scanner 14 | // before diving into the scanner itself. 15 | 16 | import ( 17 | "strconv" 18 | "sync" 19 | ) 20 | 21 | // Valid reports whether data is a valid JSON encoding. 22 | func Valid(data []byte) bool { 23 | scan := newScanner() 24 | defer freeScanner(scan) 25 | return checkValid(data, scan) == nil 26 | } 27 | 28 | // checkValid verifies that data is valid JSON-encoded data. 29 | // scan is passed in for use by checkValid to avoid an allocation. 30 | // checkValid returns nil or a SyntaxError. 31 | func checkValid(data []byte, scan *scanner) error { 32 | scan.reset() 33 | for _, c := range data { 34 | scan.bytes++ 35 | if scan.step(scan, c) == scanError { 36 | return scan.err 37 | } 38 | } 39 | if scan.eof() == scanError { 40 | return scan.err 41 | } 42 | return nil 43 | } 44 | 45 | // A SyntaxError is a description of a JSON syntax error. 46 | // Unmarshal will return a SyntaxError if the JSON can't be parsed. 47 | type SyntaxError struct { 48 | msg string // description of error 49 | Offset int64 // error occurred after reading Offset bytes 50 | } 51 | 52 | func (e *SyntaxError) Error() string { return e.msg } 53 | 54 | // A scanner is a JSON scanning state machine. 55 | // Callers call scan.reset and then pass bytes in one at a time 56 | // by calling scan.step(&scan, c) for each byte. 57 | // The return value, referred to as an opcode, tells the 58 | // caller about significant parsing events like beginning 59 | // and ending literals, objects, and arrays, so that the 60 | // caller can follow along if it wishes. 61 | // The return value scanEnd indicates that a single top-level 62 | // JSON value has been completed, *before* the byte that 63 | // just got passed in. (The indication must be delayed in order 64 | // to recognize the end of numbers: is 123 a whole value or 65 | // the beginning of 12345e+6?). 66 | type scanner struct { 67 | // The step is a func to be called to execute the next transition. 68 | // Also tried using an integer constant and a single func 69 | // with a switch, but using the func directly was 10% faster 70 | // on a 64-bit Mac Mini, and it's nicer to read. 71 | step func(*scanner, byte) int 72 | 73 | // Reached end of top-level value. 74 | endTop bool 75 | 76 | // Stack of what we're in the middle of - array values, object keys, object values. 77 | parseState []int 78 | 79 | // Error that happened, if any. 80 | err error 81 | 82 | // total bytes consumed, updated by decoder.Decode (and deliberately 83 | // not set to zero by scan.reset) 84 | bytes int64 85 | } 86 | 87 | var scannerPool = sync.Pool{ 88 | New: func() any { 89 | return &scanner{} 90 | }, 91 | } 92 | 93 | func newScanner() *scanner { 94 | scan := scannerPool.Get().(*scanner) 95 | // scan.reset by design doesn't set bytes to zero 96 | scan.bytes = 0 97 | scan.reset() 98 | return scan 99 | } 100 | 101 | func freeScanner(scan *scanner) { 102 | // Avoid hanging on to too much memory in extreme cases. 103 | if len(scan.parseState) > 1024 { 104 | scan.parseState = nil 105 | } 106 | scannerPool.Put(scan) 107 | } 108 | 109 | // These values are returned by the state transition functions 110 | // assigned to scanner.state and the method scanner.eof. 111 | // They give details about the current state of the scan that 112 | // callers might be interested to know about. 113 | // It is okay to ignore the return value of any particular 114 | // call to scanner.state: if one call returns scanError, 115 | // every subsequent call will return scanError too. 116 | const ( 117 | // Continue. 118 | scanContinue = iota // uninteresting byte 119 | scanBeginLiteral // end implied by next result != scanContinue 120 | scanBeginObject // begin object 121 | scanObjectKey // just finished object key (string) 122 | scanObjectValue // just finished non-last object value 123 | scanEndObject // end object (implies scanObjectValue if possible) 124 | scanBeginArray // begin array 125 | scanArrayValue // just finished array value 126 | scanEndArray // end array (implies scanArrayValue if possible) 127 | scanSkipSpace // space byte; can skip; known to be last "continue" result 128 | 129 | // Stop. 130 | scanEnd // top-level value ended *before* this byte; known to be first "stop" result 131 | scanError // hit an error, scanner.err. 132 | ) 133 | 134 | // These values are stored in the parseState stack. 135 | // They give the current state of a composite value 136 | // being scanned. If the parser is inside a nested value 137 | // the parseState describes the nested state, outermost at entry 0. 138 | const ( 139 | parseObjectKey = iota // parsing object key (before colon) 140 | parseObjectValue // parsing object value (after colon) 141 | parseArrayValue // parsing array value 142 | ) 143 | 144 | // This limits the max nesting depth to prevent stack overflow. 145 | // This is permitted by https://tools.ietf.org/html/rfc7159#section-9 146 | const maxNestingDepth = 10000 147 | 148 | // reset prepares the scanner for use. 149 | // It must be called before calling s.step. 150 | func (s *scanner) reset() { 151 | s.step = stateBeginValue 152 | s.parseState = s.parseState[0:0] 153 | s.err = nil 154 | s.endTop = false 155 | } 156 | 157 | // eof tells the scanner that the end of input has been reached. 158 | // It returns a scan status just as s.step does. 159 | func (s *scanner) eof() int { 160 | if s.err != nil { 161 | return scanError 162 | } 163 | if s.endTop { 164 | return scanEnd 165 | } 166 | s.step(s, ' ') 167 | if s.endTop { 168 | return scanEnd 169 | } 170 | if s.err == nil { 171 | s.err = &SyntaxError{"unexpected end of JSON input", s.bytes} 172 | } 173 | return scanError 174 | } 175 | 176 | // pushParseState pushes a new parse state p onto the parse stack. 177 | // an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned. 178 | func (s *scanner) pushParseState(c byte, newParseState int, successState int) int { 179 | s.parseState = append(s.parseState, newParseState) 180 | if len(s.parseState) <= maxNestingDepth { 181 | return successState 182 | } 183 | return s.error(c, "exceeded max depth") 184 | } 185 | 186 | // popParseState pops a parse state (already obtained) off the stack 187 | // and updates s.step accordingly. 188 | func (s *scanner) popParseState() { 189 | n := len(s.parseState) - 1 190 | s.parseState = s.parseState[0:n] 191 | if n == 0 { 192 | s.step = stateEndTop 193 | s.endTop = true 194 | } else { 195 | s.step = stateEndValue 196 | } 197 | } 198 | 199 | func isSpace(c byte) bool { 200 | return c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') 201 | } 202 | 203 | // stateBeginValueOrEmpty is the state after reading `[`. 204 | func stateBeginValueOrEmpty(s *scanner, c byte) int { 205 | if isSpace(c) { 206 | return scanSkipSpace 207 | } 208 | if c == ']' { 209 | return stateEndValue(s, c) 210 | } 211 | return stateBeginValue(s, c) 212 | } 213 | 214 | // stateBeginValue is the state at the beginning of the input. 215 | func stateBeginValue(s *scanner, c byte) int { 216 | if isSpace(c) { 217 | return scanSkipSpace 218 | } 219 | switch c { 220 | case '{': 221 | s.step = stateBeginStringOrEmpty 222 | return s.pushParseState(c, parseObjectKey, scanBeginObject) 223 | case '[': 224 | s.step = stateBeginValueOrEmpty 225 | return s.pushParseState(c, parseArrayValue, scanBeginArray) 226 | case '"': 227 | s.step = stateInString 228 | return scanBeginLiteral 229 | case '-': 230 | s.step = stateNeg 231 | return scanBeginLiteral 232 | case '0': // beginning of 0.123 233 | s.step = state0 234 | return scanBeginLiteral 235 | case 't': // beginning of true 236 | s.step = stateT 237 | return scanBeginLiteral 238 | case 'f': // beginning of false 239 | s.step = stateF 240 | return scanBeginLiteral 241 | case 'n': // beginning of null 242 | s.step = stateN 243 | return scanBeginLiteral 244 | } 245 | if '1' <= c && c <= '9' { // beginning of 1234.5 246 | s.step = state1 247 | return scanBeginLiteral 248 | } 249 | return s.error(c, "looking for beginning of value") 250 | } 251 | 252 | // stateBeginStringOrEmpty is the state after reading `{`. 253 | func stateBeginStringOrEmpty(s *scanner, c byte) int { 254 | if isSpace(c) { 255 | return scanSkipSpace 256 | } 257 | if c == '}' { 258 | n := len(s.parseState) 259 | s.parseState[n-1] = parseObjectValue 260 | return stateEndValue(s, c) 261 | } 262 | return stateBeginString(s, c) 263 | } 264 | 265 | // stateBeginString is the state after reading `{"key": value,`. 266 | func stateBeginString(s *scanner, c byte) int { 267 | if isSpace(c) { 268 | return scanSkipSpace 269 | } 270 | if c == '"' { 271 | s.step = stateInString 272 | return scanBeginLiteral 273 | } 274 | return s.error(c, "looking for beginning of object key string") 275 | } 276 | 277 | // stateEndValue is the state after completing a value, 278 | // such as after reading `{}` or `true` or `["x"`. 279 | func stateEndValue(s *scanner, c byte) int { 280 | n := len(s.parseState) 281 | if n == 0 { 282 | // Completed top-level before the current byte. 283 | s.step = stateEndTop 284 | s.endTop = true 285 | return stateEndTop(s, c) 286 | } 287 | if isSpace(c) { 288 | s.step = stateEndValue 289 | return scanSkipSpace 290 | } 291 | ps := s.parseState[n-1] 292 | switch ps { 293 | case parseObjectKey: 294 | if c == ':' { 295 | s.parseState[n-1] = parseObjectValue 296 | s.step = stateBeginValue 297 | return scanObjectKey 298 | } 299 | return s.error(c, "after object key") 300 | case parseObjectValue: 301 | if c == ',' { 302 | s.parseState[n-1] = parseObjectKey 303 | s.step = stateBeginString 304 | return scanObjectValue 305 | } 306 | if c == '}' { 307 | s.popParseState() 308 | return scanEndObject 309 | } 310 | return s.error(c, "after object key:value pair") 311 | case parseArrayValue: 312 | if c == ',' { 313 | s.step = stateBeginValue 314 | return scanArrayValue 315 | } 316 | if c == ']' { 317 | s.popParseState() 318 | return scanEndArray 319 | } 320 | return s.error(c, "after array element") 321 | } 322 | return s.error(c, "") 323 | } 324 | 325 | // stateEndTop is the state after finishing the top-level value, 326 | // such as after reading `{}` or `[1,2,3]`. 327 | // Only space characters should be seen now. 328 | func stateEndTop(s *scanner, c byte) int { 329 | if !isSpace(c) { 330 | // Complain about non-space byte on next call. 331 | s.error(c, "after top-level value") 332 | } 333 | return scanEnd 334 | } 335 | 336 | // stateInString is the state after reading `"`. 337 | func stateInString(s *scanner, c byte) int { 338 | if c == '"' { 339 | s.step = stateEndValue 340 | return scanContinue 341 | } 342 | if c == '\\' { 343 | s.step = stateInStringEsc 344 | return scanContinue 345 | } 346 | if c < 0x20 { 347 | return s.error(c, "in string literal") 348 | } 349 | return scanContinue 350 | } 351 | 352 | // stateInStringEsc is the state after reading `"\` during a quoted string. 353 | func stateInStringEsc(s *scanner, c byte) int { 354 | switch c { 355 | case 'b', 'f', 'n', 'r', 't', '\\', '/', '"': 356 | s.step = stateInString 357 | return scanContinue 358 | case 'u': 359 | s.step = stateInStringEscU 360 | return scanContinue 361 | } 362 | return s.error(c, "in string escape code") 363 | } 364 | 365 | // stateInStringEscU is the state after reading `"\u` during a quoted string. 366 | func stateInStringEscU(s *scanner, c byte) int { 367 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 368 | s.step = stateInStringEscU1 369 | return scanContinue 370 | } 371 | // numbers 372 | return s.error(c, "in \\u hexadecimal character escape") 373 | } 374 | 375 | // stateInStringEscU1 is the state after reading `"\u1` during a quoted string. 376 | func stateInStringEscU1(s *scanner, c byte) int { 377 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 378 | s.step = stateInStringEscU12 379 | return scanContinue 380 | } 381 | // numbers 382 | return s.error(c, "in \\u hexadecimal character escape") 383 | } 384 | 385 | // stateInStringEscU12 is the state after reading `"\u12` during a quoted string. 386 | func stateInStringEscU12(s *scanner, c byte) int { 387 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 388 | s.step = stateInStringEscU123 389 | return scanContinue 390 | } 391 | // numbers 392 | return s.error(c, "in \\u hexadecimal character escape") 393 | } 394 | 395 | // stateInStringEscU123 is the state after reading `"\u123` during a quoted string. 396 | func stateInStringEscU123(s *scanner, c byte) int { 397 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 398 | s.step = stateInString 399 | return scanContinue 400 | } 401 | // numbers 402 | return s.error(c, "in \\u hexadecimal character escape") 403 | } 404 | 405 | // stateNeg is the state after reading `-` during a number. 406 | func stateNeg(s *scanner, c byte) int { 407 | if c == '0' { 408 | s.step = state0 409 | return scanContinue 410 | } 411 | if '1' <= c && c <= '9' { 412 | s.step = state1 413 | return scanContinue 414 | } 415 | return s.error(c, "in numeric literal") 416 | } 417 | 418 | // state1 is the state after reading a non-zero integer during a number, 419 | // such as after reading `1` or `100` but not `0`. 420 | func state1(s *scanner, c byte) int { 421 | if '0' <= c && c <= '9' { 422 | s.step = state1 423 | return scanContinue 424 | } 425 | return state0(s, c) 426 | } 427 | 428 | // state0 is the state after reading `0` during a number. 429 | func state0(s *scanner, c byte) int { 430 | if c == '.' { 431 | s.step = stateDot 432 | return scanContinue 433 | } 434 | if c == 'e' || c == 'E' { 435 | s.step = stateE 436 | return scanContinue 437 | } 438 | return stateEndValue(s, c) 439 | } 440 | 441 | // stateDot is the state after reading the integer and decimal point in a number, 442 | // such as after reading `1.`. 443 | func stateDot(s *scanner, c byte) int { 444 | if '0' <= c && c <= '9' { 445 | s.step = stateDot0 446 | return scanContinue 447 | } 448 | return s.error(c, "after decimal point in numeric literal") 449 | } 450 | 451 | // stateDot0 is the state after reading the integer, decimal point, and subsequent 452 | // digits of a number, such as after reading `3.14`. 453 | func stateDot0(s *scanner, c byte) int { 454 | if '0' <= c && c <= '9' { 455 | return scanContinue 456 | } 457 | if c == 'e' || c == 'E' { 458 | s.step = stateE 459 | return scanContinue 460 | } 461 | return stateEndValue(s, c) 462 | } 463 | 464 | // stateE is the state after reading the mantissa and e in a number, 465 | // such as after reading `314e` or `0.314e`. 466 | func stateE(s *scanner, c byte) int { 467 | if c == '+' || c == '-' { 468 | s.step = stateESign 469 | return scanContinue 470 | } 471 | return stateESign(s, c) 472 | } 473 | 474 | // stateESign is the state after reading the mantissa, e, and sign in a number, 475 | // such as after reading `314e-` or `0.314e+`. 476 | func stateESign(s *scanner, c byte) int { 477 | if '0' <= c && c <= '9' { 478 | s.step = stateE0 479 | return scanContinue 480 | } 481 | return s.error(c, "in exponent of numeric literal") 482 | } 483 | 484 | // stateE0 is the state after reading the mantissa, e, optional sign, 485 | // and at least one digit of the exponent in a number, 486 | // such as after reading `314e-2` or `0.314e+1` or `3.14e0`. 487 | func stateE0(s *scanner, c byte) int { 488 | if '0' <= c && c <= '9' { 489 | return scanContinue 490 | } 491 | return stateEndValue(s, c) 492 | } 493 | 494 | // stateT is the state after reading `t`. 495 | func stateT(s *scanner, c byte) int { 496 | if c == 'r' { 497 | s.step = stateTr 498 | return scanContinue 499 | } 500 | return s.error(c, "in literal true (expecting 'r')") 501 | } 502 | 503 | // stateTr is the state after reading `tr`. 504 | func stateTr(s *scanner, c byte) int { 505 | if c == 'u' { 506 | s.step = stateTru 507 | return scanContinue 508 | } 509 | return s.error(c, "in literal true (expecting 'u')") 510 | } 511 | 512 | // stateTru is the state after reading `tru`. 513 | func stateTru(s *scanner, c byte) int { 514 | if c == 'e' { 515 | s.step = stateEndValue 516 | return scanContinue 517 | } 518 | return s.error(c, "in literal true (expecting 'e')") 519 | } 520 | 521 | // stateF is the state after reading `f`. 522 | func stateF(s *scanner, c byte) int { 523 | if c == 'a' { 524 | s.step = stateFa 525 | return scanContinue 526 | } 527 | return s.error(c, "in literal false (expecting 'a')") 528 | } 529 | 530 | // stateFa is the state after reading `fa`. 531 | func stateFa(s *scanner, c byte) int { 532 | if c == 'l' { 533 | s.step = stateFal 534 | return scanContinue 535 | } 536 | return s.error(c, "in literal false (expecting 'l')") 537 | } 538 | 539 | // stateFal is the state after reading `fal`. 540 | func stateFal(s *scanner, c byte) int { 541 | if c == 's' { 542 | s.step = stateFals 543 | return scanContinue 544 | } 545 | return s.error(c, "in literal false (expecting 's')") 546 | } 547 | 548 | // stateFals is the state after reading `fals`. 549 | func stateFals(s *scanner, c byte) int { 550 | if c == 'e' { 551 | s.step = stateEndValue 552 | return scanContinue 553 | } 554 | return s.error(c, "in literal false (expecting 'e')") 555 | } 556 | 557 | // stateN is the state after reading `n`. 558 | func stateN(s *scanner, c byte) int { 559 | if c == 'u' { 560 | s.step = stateNu 561 | return scanContinue 562 | } 563 | return s.error(c, "in literal null (expecting 'u')") 564 | } 565 | 566 | // stateNu is the state after reading `nu`. 567 | func stateNu(s *scanner, c byte) int { 568 | if c == 'l' { 569 | s.step = stateNul 570 | return scanContinue 571 | } 572 | return s.error(c, "in literal null (expecting 'l')") 573 | } 574 | 575 | // stateNul is the state after reading `nul`. 576 | func stateNul(s *scanner, c byte) int { 577 | if c == 'l' { 578 | s.step = stateEndValue 579 | return scanContinue 580 | } 581 | return s.error(c, "in literal null (expecting 'l')") 582 | } 583 | 584 | // stateError is the state after reaching a syntax error, 585 | // such as after reading `[1}` or `5.1.2`. 586 | func stateError(s *scanner, c byte) int { 587 | return scanError 588 | } 589 | 590 | // error records an error and switches to the error state. 591 | func (s *scanner) error(c byte, context string) int { 592 | s.step = stateError 593 | s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes} 594 | return scanError 595 | } 596 | 597 | // quoteChar formats c as a quoted character literal. 598 | func quoteChar(c byte) string { 599 | // special cases - different from quoted strings 600 | if c == '\'' { 601 | return `'\''` 602 | } 603 | if c == '"' { 604 | return `'"'` 605 | } 606 | 607 | // use quoted string with different quotation marks 608 | s := strconv.Quote(string(c)) 609 | return "'" + s[1:len(s)-1] + "'" 610 | } 611 | -------------------------------------------------------------------------------- /v5/internal/json/scanner_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "math" 10 | "math/rand" 11 | "reflect" 12 | "testing" 13 | ) 14 | 15 | var validTests = []struct { 16 | data string 17 | ok bool 18 | }{ 19 | {`foo`, false}, 20 | {`}{`, false}, 21 | {`{]`, false}, 22 | {`{}`, true}, 23 | {`{"foo":"bar"}`, true}, 24 | {`{"foo":"bar","bar":{"baz":["qux"]}}`, true}, 25 | } 26 | 27 | func TestValid(t *testing.T) { 28 | for _, tt := range validTests { 29 | if ok := Valid([]byte(tt.data)); ok != tt.ok { 30 | t.Errorf("Valid(%#q) = %v, want %v", tt.data, ok, tt.ok) 31 | } 32 | } 33 | } 34 | 35 | // Tests of simple examples. 36 | 37 | type example struct { 38 | compact string 39 | indent string 40 | } 41 | 42 | var examples = []example{ 43 | {`1`, `1`}, 44 | {`{}`, `{}`}, 45 | {`[]`, `[]`}, 46 | {`{"":2}`, "{\n\t\"\": 2\n}"}, 47 | {`[3]`, "[\n\t3\n]"}, 48 | {`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"}, 49 | {`{"x":1}`, "{\n\t\"x\": 1\n}"}, 50 | {ex1, ex1i}, 51 | {"{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070 52 | } 53 | 54 | var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]` 55 | 56 | var ex1i = `[ 57 | true, 58 | false, 59 | null, 60 | "x", 61 | 1, 62 | 1.5, 63 | 0, 64 | -5e+2 65 | ]` 66 | 67 | func TestCompact(t *testing.T) { 68 | var buf bytes.Buffer 69 | for _, tt := range examples { 70 | buf.Reset() 71 | if err := Compact(&buf, []byte(tt.compact)); err != nil { 72 | t.Errorf("Compact(%#q): %v", tt.compact, err) 73 | } else if s := buf.String(); s != tt.compact { 74 | t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s) 75 | } 76 | 77 | buf.Reset() 78 | if err := Compact(&buf, []byte(tt.indent)); err != nil { 79 | t.Errorf("Compact(%#q): %v", tt.indent, err) 80 | continue 81 | } else if s := buf.String(); s != tt.compact { 82 | t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact) 83 | } 84 | } 85 | } 86 | 87 | func TestCompactSeparators(t *testing.T) { 88 | // U+2028 and U+2029 should be escaped inside strings. 89 | // They should not appear outside strings. 90 | tests := []struct { 91 | in, compact string 92 | }{ 93 | {"{\"\u2028\": 1}", "{\"\u2028\":1}"}, 94 | {"{\"\u2029\" :2}", "{\"\u2029\":2}"}, 95 | } 96 | for _, tt := range tests { 97 | var buf bytes.Buffer 98 | if err := Compact(&buf, []byte(tt.in)); err != nil { 99 | t.Errorf("Compact(%q): %v", tt.in, err) 100 | } else if s := buf.String(); s != tt.compact { 101 | t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact) 102 | } 103 | } 104 | } 105 | 106 | func TestIndent(t *testing.T) { 107 | var buf bytes.Buffer 108 | for _, tt := range examples { 109 | buf.Reset() 110 | if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil { 111 | t.Errorf("Indent(%#q): %v", tt.indent, err) 112 | } else if s := buf.String(); s != tt.indent { 113 | t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s) 114 | } 115 | 116 | buf.Reset() 117 | if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil { 118 | t.Errorf("Indent(%#q): %v", tt.compact, err) 119 | continue 120 | } else if s := buf.String(); s != tt.indent { 121 | t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent) 122 | } 123 | } 124 | } 125 | 126 | // Tests of a large random structure. 127 | 128 | func TestCompactBig(t *testing.T) { 129 | initBig() 130 | var buf bytes.Buffer 131 | if err := Compact(&buf, jsonBig); err != nil { 132 | t.Fatalf("Compact: %v", err) 133 | } 134 | b := buf.Bytes() 135 | if !bytes.Equal(b, jsonBig) { 136 | t.Error("Compact(jsonBig) != jsonBig") 137 | diff(t, b, jsonBig) 138 | return 139 | } 140 | } 141 | 142 | func TestIndentBig(t *testing.T) { 143 | t.Parallel() 144 | initBig() 145 | var buf bytes.Buffer 146 | if err := Indent(&buf, jsonBig, "", "\t"); err != nil { 147 | t.Fatalf("Indent1: %v", err) 148 | } 149 | b := buf.Bytes() 150 | if len(b) == len(jsonBig) { 151 | // jsonBig is compact (no unnecessary spaces); 152 | // indenting should make it bigger 153 | t.Fatalf("Indent(jsonBig) did not get bigger") 154 | } 155 | 156 | // should be idempotent 157 | var buf1 bytes.Buffer 158 | if err := Indent(&buf1, b, "", "\t"); err != nil { 159 | t.Fatalf("Indent2: %v", err) 160 | } 161 | b1 := buf1.Bytes() 162 | if !bytes.Equal(b1, b) { 163 | t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)") 164 | diff(t, b1, b) 165 | return 166 | } 167 | 168 | // should get back to original 169 | buf1.Reset() 170 | if err := Compact(&buf1, b); err != nil { 171 | t.Fatalf("Compact: %v", err) 172 | } 173 | b1 = buf1.Bytes() 174 | if !bytes.Equal(b1, jsonBig) { 175 | t.Error("Compact(Indent(jsonBig)) != jsonBig") 176 | diff(t, b1, jsonBig) 177 | return 178 | } 179 | } 180 | 181 | type indentErrorTest struct { 182 | in string 183 | err error 184 | } 185 | 186 | var indentErrorTests = []indentErrorTest{ 187 | {`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}}, 188 | {`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}}, 189 | } 190 | 191 | func TestIndentErrors(t *testing.T) { 192 | for i, tt := range indentErrorTests { 193 | slice := make([]uint8, 0) 194 | buf := bytes.NewBuffer(slice) 195 | if err := Indent(buf, []uint8(tt.in), "", ""); err != nil { 196 | if !reflect.DeepEqual(err, tt.err) { 197 | t.Errorf("#%d: Indent: %#v", i, err) 198 | continue 199 | } 200 | } 201 | } 202 | } 203 | 204 | func diff(t *testing.T, a, b []byte) { 205 | for i := 0; ; i++ { 206 | if i >= len(a) || i >= len(b) || a[i] != b[i] { 207 | j := i - 10 208 | if j < 0 { 209 | j = 0 210 | } 211 | t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:])) 212 | return 213 | } 214 | } 215 | } 216 | 217 | func trim(b []byte) []byte { 218 | if len(b) > 20 { 219 | return b[0:20] 220 | } 221 | return b 222 | } 223 | 224 | // Generate a random JSON object. 225 | 226 | var jsonBig []byte 227 | 228 | func initBig() { 229 | n := 10000 230 | if testing.Short() { 231 | n = 100 232 | } 233 | b, err := Marshal(genValue(n)) 234 | if err != nil { 235 | panic(err) 236 | } 237 | jsonBig = b 238 | } 239 | 240 | func genValue(n int) any { 241 | if n > 1 { 242 | switch rand.Intn(2) { 243 | case 0: 244 | return genArray(n) 245 | case 1: 246 | return genMap(n) 247 | } 248 | } 249 | switch rand.Intn(3) { 250 | case 0: 251 | return rand.Intn(2) == 0 252 | case 1: 253 | return rand.NormFloat64() 254 | case 2: 255 | return genString(30) 256 | } 257 | panic("unreachable") 258 | } 259 | 260 | func genString(stddev float64) string { 261 | n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2)) 262 | c := make([]rune, n) 263 | for i := range c { 264 | f := math.Abs(rand.NormFloat64()*64 + 32) 265 | if f > 0x10ffff { 266 | f = 0x10ffff 267 | } 268 | c[i] = rune(f) 269 | } 270 | return string(c) 271 | } 272 | 273 | func genArray(n int) []any { 274 | f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) 275 | if f > n { 276 | f = n 277 | } 278 | if f < 1 { 279 | f = 1 280 | } 281 | x := make([]any, f) 282 | for i := range x { 283 | x[i] = genValue(((i+1)*n)/f - (i*n)/f) 284 | } 285 | return x 286 | } 287 | 288 | func genMap(n int) map[string]any { 289 | f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) 290 | if f > n { 291 | f = n 292 | } 293 | if n > 0 && f == 0 { 294 | f = 1 295 | } 296 | x := make(map[string]any) 297 | for i := 0; i < f; i++ { 298 | x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f) 299 | } 300 | return x 301 | } 302 | -------------------------------------------------------------------------------- /v5/internal/json/stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "io" 11 | ) 12 | 13 | // A Decoder reads and decodes JSON values from an input stream. 14 | type Decoder struct { 15 | r io.Reader 16 | buf []byte 17 | d decodeState 18 | scanp int // start of unread data in buf 19 | scanned int64 // amount of data already scanned 20 | scan scanner 21 | err error 22 | 23 | tokenState int 24 | tokenStack []int 25 | } 26 | 27 | // NewDecoder returns a new decoder that reads from r. 28 | // 29 | // The decoder introduces its own buffering and may 30 | // read data from r beyond the JSON values requested. 31 | func NewDecoder(r io.Reader) *Decoder { 32 | return &Decoder{r: r} 33 | } 34 | 35 | // UseNumber causes the Decoder to unmarshal a number into an interface{} as a 36 | // Number instead of as a float64. 37 | func (dec *Decoder) UseNumber() { dec.d.useNumber = true } 38 | 39 | // DisallowUnknownFields causes the Decoder to return an error when the destination 40 | // is a struct and the input contains object keys which do not match any 41 | // non-ignored, exported fields in the destination. 42 | func (dec *Decoder) DisallowUnknownFields() { dec.d.disallowUnknownFields = true } 43 | 44 | // Decode reads the next JSON-encoded value from its 45 | // input and stores it in the value pointed to by v. 46 | // 47 | // See the documentation for Unmarshal for details about 48 | // the conversion of JSON into a Go value. 49 | func (dec *Decoder) Decode(v any) error { 50 | if dec.err != nil { 51 | return dec.err 52 | } 53 | 54 | if err := dec.tokenPrepareForDecode(); err != nil { 55 | return err 56 | } 57 | 58 | if !dec.tokenValueAllowed() { 59 | return &SyntaxError{msg: "not at beginning of value", Offset: dec.InputOffset()} 60 | } 61 | 62 | // Read whole value into buffer. 63 | n, err := dec.readValue() 64 | if err != nil { 65 | return err 66 | } 67 | dec.d.init(dec.buf[dec.scanp : dec.scanp+n]) 68 | dec.scanp += n 69 | 70 | // Don't save err from unmarshal into dec.err: 71 | // the connection is still usable since we read a complete JSON 72 | // object from it before the error happened. 73 | err = dec.d.unmarshal(v) 74 | 75 | // fixup token streaming state 76 | dec.tokenValueEnd() 77 | 78 | return err 79 | } 80 | 81 | // Buffered returns a reader of the data remaining in the Decoder's 82 | // buffer. The reader is valid until the next call to Decode. 83 | func (dec *Decoder) Buffered() io.Reader { 84 | return bytes.NewReader(dec.buf[dec.scanp:]) 85 | } 86 | 87 | // readValue reads a JSON value into dec.buf. 88 | // It returns the length of the encoding. 89 | func (dec *Decoder) readValue() (int, error) { 90 | dec.scan.reset() 91 | 92 | scanp := dec.scanp 93 | var err error 94 | Input: 95 | // help the compiler see that scanp is never negative, so it can remove 96 | // some bounds checks below. 97 | for scanp >= 0 { 98 | 99 | // Look in the buffer for a new value. 100 | for ; scanp < len(dec.buf); scanp++ { 101 | c := dec.buf[scanp] 102 | dec.scan.bytes++ 103 | switch dec.scan.step(&dec.scan, c) { 104 | case scanEnd: 105 | // scanEnd is delayed one byte so we decrement 106 | // the scanner bytes count by 1 to ensure that 107 | // this value is correct in the next call of Decode. 108 | dec.scan.bytes-- 109 | break Input 110 | case scanEndObject, scanEndArray: 111 | // scanEnd is delayed one byte. 112 | // We might block trying to get that byte from src, 113 | // so instead invent a space byte. 114 | if stateEndValue(&dec.scan, ' ') == scanEnd { 115 | scanp++ 116 | break Input 117 | } 118 | case scanError: 119 | dec.err = dec.scan.err 120 | return 0, dec.scan.err 121 | } 122 | } 123 | 124 | // Did the last read have an error? 125 | // Delayed until now to allow buffer scan. 126 | if err != nil { 127 | if err == io.EOF { 128 | if dec.scan.step(&dec.scan, ' ') == scanEnd { 129 | break Input 130 | } 131 | if nonSpace(dec.buf) { 132 | err = io.ErrUnexpectedEOF 133 | } 134 | } 135 | dec.err = err 136 | return 0, err 137 | } 138 | 139 | n := scanp - dec.scanp 140 | err = dec.refill() 141 | scanp = dec.scanp + n 142 | } 143 | return scanp - dec.scanp, nil 144 | } 145 | 146 | func (dec *Decoder) refill() error { 147 | // Make room to read more into the buffer. 148 | // First slide down data already consumed. 149 | if dec.scanp > 0 { 150 | dec.scanned += int64(dec.scanp) 151 | n := copy(dec.buf, dec.buf[dec.scanp:]) 152 | dec.buf = dec.buf[:n] 153 | dec.scanp = 0 154 | } 155 | 156 | // Grow buffer if not large enough. 157 | const minRead = 512 158 | if cap(dec.buf)-len(dec.buf) < minRead { 159 | newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead) 160 | copy(newBuf, dec.buf) 161 | dec.buf = newBuf 162 | } 163 | 164 | // Read. Delay error for next iteration (after scan). 165 | n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)]) 166 | dec.buf = dec.buf[0 : len(dec.buf)+n] 167 | 168 | return err 169 | } 170 | 171 | func nonSpace(b []byte) bool { 172 | for _, c := range b { 173 | if !isSpace(c) { 174 | return true 175 | } 176 | } 177 | return false 178 | } 179 | 180 | // An Encoder writes JSON values to an output stream. 181 | type Encoder struct { 182 | w io.Writer 183 | err error 184 | escapeHTML bool 185 | 186 | indentBuf *bytes.Buffer 187 | indentPrefix string 188 | indentValue string 189 | } 190 | 191 | // NewEncoder returns a new encoder that writes to w. 192 | func NewEncoder(w io.Writer) *Encoder { 193 | return &Encoder{w: w, escapeHTML: true} 194 | } 195 | 196 | // Encode writes the JSON encoding of v to the stream, 197 | // followed by a newline character. 198 | // 199 | // See the documentation for Marshal for details about the 200 | // conversion of Go values to JSON. 201 | func (enc *Encoder) Encode(v any) error { 202 | if enc.err != nil { 203 | return enc.err 204 | } 205 | 206 | e := newEncodeState() 207 | defer encodeStatePool.Put(e) 208 | 209 | err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML}) 210 | if err != nil { 211 | return err 212 | } 213 | 214 | // Terminate each value with a newline. 215 | // This makes the output look a little nicer 216 | // when debugging, and some kind of space 217 | // is required if the encoded value was a number, 218 | // so that the reader knows there aren't more 219 | // digits coming. 220 | e.WriteByte('\n') 221 | 222 | b := e.Bytes() 223 | if enc.indentPrefix != "" || enc.indentValue != "" { 224 | if enc.indentBuf == nil { 225 | enc.indentBuf = new(bytes.Buffer) 226 | } 227 | enc.indentBuf.Reset() 228 | err = Indent(enc.indentBuf, b, enc.indentPrefix, enc.indentValue) 229 | if err != nil { 230 | return err 231 | } 232 | b = enc.indentBuf.Bytes() 233 | } 234 | if _, err = enc.w.Write(b); err != nil { 235 | enc.err = err 236 | } 237 | return err 238 | } 239 | 240 | // SetIndent instructs the encoder to format each subsequent encoded 241 | // value as if indented by the package-level function Indent(dst, src, prefix, indent). 242 | // Calling SetIndent("", "") disables indentation. 243 | func (enc *Encoder) SetIndent(prefix, indent string) { 244 | enc.indentPrefix = prefix 245 | enc.indentValue = indent 246 | } 247 | 248 | // SetEscapeHTML specifies whether problematic HTML characters 249 | // should be escaped inside JSON quoted strings. 250 | // The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e 251 | // to avoid certain safety problems that can arise when embedding JSON in HTML. 252 | // 253 | // In non-HTML settings where the escaping interferes with the readability 254 | // of the output, SetEscapeHTML(false) disables this behavior. 255 | func (enc *Encoder) SetEscapeHTML(on bool) { 256 | enc.escapeHTML = on 257 | } 258 | 259 | // RawMessage is a raw encoded JSON value. 260 | // It implements Marshaler and Unmarshaler and can 261 | // be used to delay JSON decoding or precompute a JSON encoding. 262 | type RawMessage = json.RawMessage 263 | 264 | // A Token holds a value of one of these types: 265 | // 266 | // Delim, for the four JSON delimiters [ ] { } 267 | // bool, for JSON booleans 268 | // float64, for JSON numbers 269 | // Number, for JSON numbers 270 | // string, for JSON string literals 271 | // nil, for JSON null 272 | type Token any 273 | 274 | const ( 275 | tokenTopValue = iota 276 | tokenArrayStart 277 | tokenArrayValue 278 | tokenArrayComma 279 | tokenObjectStart 280 | tokenObjectKey 281 | tokenObjectColon 282 | tokenObjectValue 283 | tokenObjectComma 284 | ) 285 | 286 | // advance tokenstate from a separator state to a value state 287 | func (dec *Decoder) tokenPrepareForDecode() error { 288 | // Note: Not calling peek before switch, to avoid 289 | // putting peek into the standard Decode path. 290 | // peek is only called when using the Token API. 291 | switch dec.tokenState { 292 | case tokenArrayComma: 293 | c, err := dec.peek() 294 | if err != nil { 295 | return err 296 | } 297 | if c != ',' { 298 | return &SyntaxError{"expected comma after array element", dec.InputOffset()} 299 | } 300 | dec.scanp++ 301 | dec.tokenState = tokenArrayValue 302 | case tokenObjectColon: 303 | c, err := dec.peek() 304 | if err != nil { 305 | return err 306 | } 307 | if c != ':' { 308 | return &SyntaxError{"expected colon after object key", dec.InputOffset()} 309 | } 310 | dec.scanp++ 311 | dec.tokenState = tokenObjectValue 312 | } 313 | return nil 314 | } 315 | 316 | func (dec *Decoder) tokenValueAllowed() bool { 317 | switch dec.tokenState { 318 | case tokenTopValue, tokenArrayStart, tokenArrayValue, tokenObjectValue: 319 | return true 320 | } 321 | return false 322 | } 323 | 324 | func (dec *Decoder) tokenValueEnd() { 325 | switch dec.tokenState { 326 | case tokenArrayStart, tokenArrayValue: 327 | dec.tokenState = tokenArrayComma 328 | case tokenObjectValue: 329 | dec.tokenState = tokenObjectComma 330 | } 331 | } 332 | 333 | // A Delim is a JSON array or object delimiter, one of [ ] { or }. 334 | type Delim rune 335 | 336 | func (d Delim) String() string { 337 | return string(d) 338 | } 339 | 340 | // Token returns the next JSON token in the input stream. 341 | // At the end of the input stream, Token returns nil, io.EOF. 342 | // 343 | // Token guarantees that the delimiters [ ] { } it returns are 344 | // properly nested and matched: if Token encounters an unexpected 345 | // delimiter in the input, it will return an error. 346 | // 347 | // The input stream consists of basic JSON values—bool, string, 348 | // number, and null—along with delimiters [ ] { } of type Delim 349 | // to mark the start and end of arrays and objects. 350 | // Commas and colons are elided. 351 | func (dec *Decoder) Token() (Token, error) { 352 | for { 353 | c, err := dec.peek() 354 | if err != nil { 355 | return nil, err 356 | } 357 | switch c { 358 | case '[': 359 | if !dec.tokenValueAllowed() { 360 | return dec.tokenError(c) 361 | } 362 | dec.scanp++ 363 | dec.tokenStack = append(dec.tokenStack, dec.tokenState) 364 | dec.tokenState = tokenArrayStart 365 | return Delim('['), nil 366 | 367 | case ']': 368 | if dec.tokenState != tokenArrayStart && dec.tokenState != tokenArrayComma { 369 | return dec.tokenError(c) 370 | } 371 | dec.scanp++ 372 | dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1] 373 | dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1] 374 | dec.tokenValueEnd() 375 | return Delim(']'), nil 376 | 377 | case '{': 378 | if !dec.tokenValueAllowed() { 379 | return dec.tokenError(c) 380 | } 381 | dec.scanp++ 382 | dec.tokenStack = append(dec.tokenStack, dec.tokenState) 383 | dec.tokenState = tokenObjectStart 384 | return Delim('{'), nil 385 | 386 | case '}': 387 | if dec.tokenState != tokenObjectStart && dec.tokenState != tokenObjectComma { 388 | return dec.tokenError(c) 389 | } 390 | dec.scanp++ 391 | dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1] 392 | dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1] 393 | dec.tokenValueEnd() 394 | return Delim('}'), nil 395 | 396 | case ':': 397 | if dec.tokenState != tokenObjectColon { 398 | return dec.tokenError(c) 399 | } 400 | dec.scanp++ 401 | dec.tokenState = tokenObjectValue 402 | continue 403 | 404 | case ',': 405 | if dec.tokenState == tokenArrayComma { 406 | dec.scanp++ 407 | dec.tokenState = tokenArrayValue 408 | continue 409 | } 410 | if dec.tokenState == tokenObjectComma { 411 | dec.scanp++ 412 | dec.tokenState = tokenObjectKey 413 | continue 414 | } 415 | return dec.tokenError(c) 416 | 417 | case '"': 418 | if dec.tokenState == tokenObjectStart || dec.tokenState == tokenObjectKey { 419 | var x string 420 | old := dec.tokenState 421 | dec.tokenState = tokenTopValue 422 | err := dec.Decode(&x) 423 | dec.tokenState = old 424 | if err != nil { 425 | return nil, err 426 | } 427 | dec.tokenState = tokenObjectColon 428 | return x, nil 429 | } 430 | fallthrough 431 | 432 | default: 433 | if !dec.tokenValueAllowed() { 434 | return dec.tokenError(c) 435 | } 436 | var x any 437 | if err := dec.Decode(&x); err != nil { 438 | return nil, err 439 | } 440 | return x, nil 441 | } 442 | } 443 | } 444 | 445 | func (dec *Decoder) tokenError(c byte) (Token, error) { 446 | var context string 447 | switch dec.tokenState { 448 | case tokenTopValue: 449 | context = " looking for beginning of value" 450 | case tokenArrayStart, tokenArrayValue, tokenObjectValue: 451 | context = " looking for beginning of value" 452 | case tokenArrayComma: 453 | context = " after array element" 454 | case tokenObjectKey: 455 | context = " looking for beginning of object key string" 456 | case tokenObjectColon: 457 | context = " after object key" 458 | case tokenObjectComma: 459 | context = " after object key:value pair" 460 | } 461 | return nil, &SyntaxError{"invalid character " + quoteChar(c) + context, dec.InputOffset()} 462 | } 463 | 464 | // More reports whether there is another element in the 465 | // current array or object being parsed. 466 | func (dec *Decoder) More() bool { 467 | c, err := dec.peek() 468 | return err == nil && c != ']' && c != '}' 469 | } 470 | 471 | func (dec *Decoder) peek() (byte, error) { 472 | var err error 473 | for { 474 | for i := dec.scanp; i < len(dec.buf); i++ { 475 | c := dec.buf[i] 476 | if isSpace(c) { 477 | continue 478 | } 479 | dec.scanp = i 480 | return c, nil 481 | } 482 | // buffer has been scanned, now report any error 483 | if err != nil { 484 | return 0, err 485 | } 486 | err = dec.refill() 487 | } 488 | } 489 | 490 | // InputOffset returns the input stream byte offset of the current decoder position. 491 | // The offset gives the location of the end of the most recently returned token 492 | // and the beginning of the next token. 493 | func (dec *Decoder) InputOffset() int64 { 494 | return dec.scanned + int64(dec.scanp) 495 | } 496 | -------------------------------------------------------------------------------- /v5/internal/json/stream_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "log" 11 | "net" 12 | "net/http" 13 | "net/http/httptest" 14 | "reflect" 15 | "runtime/debug" 16 | "strings" 17 | "testing" 18 | ) 19 | 20 | // Test values for the stream test. 21 | // One of each JSON kind. 22 | var streamTest = []any{ 23 | 0.1, 24 | "hello", 25 | nil, 26 | true, 27 | false, 28 | []any{"a", "b", "c"}, 29 | map[string]any{"K": "Kelvin", "ß": "long s"}, 30 | 3.14, // another value to make sure something can follow map 31 | } 32 | 33 | var streamEncoded = `0.1 34 | "hello" 35 | null 36 | true 37 | false 38 | ["a","b","c"] 39 | {"ß":"long s","K":"Kelvin"} 40 | 3.14 41 | ` 42 | 43 | func TestEncoder(t *testing.T) { 44 | for i := 0; i <= len(streamTest); i++ { 45 | var buf strings.Builder 46 | enc := NewEncoder(&buf) 47 | // Check that enc.SetIndent("", "") turns off indentation. 48 | enc.SetIndent(">", ".") 49 | enc.SetIndent("", "") 50 | for j, v := range streamTest[0:i] { 51 | if err := enc.Encode(v); err != nil { 52 | t.Fatalf("encode #%d: %v", j, err) 53 | } 54 | } 55 | if have, want := buf.String(), nlines(streamEncoded, i); have != want { 56 | t.Errorf("encoding %d items: mismatch", i) 57 | diff(t, []byte(have), []byte(want)) 58 | break 59 | } 60 | } 61 | } 62 | 63 | func TestEncoderErrorAndReuseEncodeState(t *testing.T) { 64 | // Disable the GC temporarily to prevent encodeState's in Pool being cleaned away during the test. 65 | percent := debug.SetGCPercent(-1) 66 | defer debug.SetGCPercent(percent) 67 | 68 | // Trigger an error in Marshal with cyclic data. 69 | type Dummy struct { 70 | Name string 71 | Next *Dummy 72 | } 73 | dummy := Dummy{Name: "Dummy"} 74 | dummy.Next = &dummy 75 | 76 | var buf bytes.Buffer 77 | enc := NewEncoder(&buf) 78 | if err := enc.Encode(dummy); err == nil { 79 | t.Errorf("Encode(dummy) == nil; want error") 80 | } 81 | 82 | type Data struct { 83 | A string 84 | I int 85 | } 86 | data := Data{A: "a", I: 1} 87 | if err := enc.Encode(data); err != nil { 88 | t.Errorf("Marshal(%v) = %v", data, err) 89 | } 90 | 91 | var data2 Data 92 | if err := Unmarshal(buf.Bytes(), &data2); err != nil { 93 | t.Errorf("Unmarshal(%v) = %v", data2, err) 94 | } 95 | if data2 != data { 96 | t.Errorf("expect: %v, but get: %v", data, data2) 97 | } 98 | } 99 | 100 | var streamEncodedIndent = `0.1 101 | "hello" 102 | null 103 | true 104 | false 105 | [ 106 | >."a", 107 | >."b", 108 | >."c" 109 | >] 110 | { 111 | >."ß": "long s", 112 | >."K": "Kelvin" 113 | >} 114 | 3.14 115 | ` 116 | 117 | func TestEncoderIndent(t *testing.T) { 118 | var buf strings.Builder 119 | enc := NewEncoder(&buf) 120 | enc.SetIndent(">", ".") 121 | for _, v := range streamTest { 122 | enc.Encode(v) 123 | } 124 | if have, want := buf.String(), streamEncodedIndent; have != want { 125 | t.Error("indented encoding mismatch") 126 | diff(t, []byte(have), []byte(want)) 127 | } 128 | } 129 | 130 | type strMarshaler string 131 | 132 | func (s strMarshaler) MarshalJSON() ([]byte, error) { 133 | return []byte(s), nil 134 | } 135 | 136 | type strPtrMarshaler string 137 | 138 | func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) { 139 | return []byte(*s), nil 140 | } 141 | 142 | func TestEncoderSetEscapeHTML(t *testing.T) { 143 | var c C 144 | var ct CText 145 | var tagStruct struct { 146 | Valid int `json:"<>&#! "` 147 | Invalid int `json:"\\"` 148 | } 149 | 150 | // This case is particularly interesting, as we force the encoder to 151 | // take the address of the Ptr field to use its MarshalJSON method. This 152 | // is why the '&' is important. 153 | marshalerStruct := &struct { 154 | NonPtr strMarshaler 155 | Ptr strPtrMarshaler 156 | }{`""`, `""`} 157 | 158 | // https://golang.org/issue/34154 159 | stringOption := struct { 160 | Bar string `json:"bar,string"` 161 | }{`foobar`} 162 | 163 | for _, tt := range []struct { 164 | name string 165 | v any 166 | wantEscape string 167 | want string 168 | }{ 169 | {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`}, 170 | {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`}, 171 | {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`}, 172 | { 173 | "tagStruct", tagStruct, 174 | `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`, 175 | `{"<>&#! ":0,"Invalid":0}`, 176 | }, 177 | { 178 | `""`, marshalerStruct, 179 | `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`, 180 | `{"NonPtr":"","Ptr":""}`, 181 | }, 182 | { 183 | "stringOption", stringOption, 184 | `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`, 185 | `{"bar":"\"foobar\""}`, 186 | }, 187 | } { 188 | var buf strings.Builder 189 | enc := NewEncoder(&buf) 190 | if err := enc.Encode(tt.v); err != nil { 191 | t.Errorf("Encode(%s): %s", tt.name, err) 192 | continue 193 | } 194 | if got := strings.TrimSpace(buf.String()); got != tt.wantEscape { 195 | t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape) 196 | } 197 | buf.Reset() 198 | enc.SetEscapeHTML(false) 199 | if err := enc.Encode(tt.v); err != nil { 200 | t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err) 201 | continue 202 | } 203 | if got := strings.TrimSpace(buf.String()); got != tt.want { 204 | t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q", 205 | tt.name, got, tt.want) 206 | } 207 | } 208 | } 209 | 210 | func TestDecoder(t *testing.T) { 211 | for i := 0; i <= len(streamTest); i++ { 212 | // Use stream without newlines as input, 213 | // just to stress the decoder even more. 214 | // Our test input does not include back-to-back numbers. 215 | // Otherwise stripping the newlines would 216 | // merge two adjacent JSON values. 217 | var buf bytes.Buffer 218 | for _, c := range nlines(streamEncoded, i) { 219 | if c != '\n' { 220 | buf.WriteRune(c) 221 | } 222 | } 223 | out := make([]any, i) 224 | dec := NewDecoder(&buf) 225 | for j := range out { 226 | if err := dec.Decode(&out[j]); err != nil { 227 | t.Fatalf("decode #%d/%d: %v", j, i, err) 228 | } 229 | } 230 | if !reflect.DeepEqual(out, streamTest[0:i]) { 231 | t.Errorf("decoding %d items: mismatch", i) 232 | for j := range out { 233 | if !reflect.DeepEqual(out[j], streamTest[j]) { 234 | t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j]) 235 | } 236 | } 237 | break 238 | } 239 | } 240 | } 241 | 242 | func TestDecoderBuffered(t *testing.T) { 243 | r := strings.NewReader(`{"Name": "Gopher"} extra `) 244 | var m struct { 245 | Name string 246 | } 247 | d := NewDecoder(r) 248 | err := d.Decode(&m) 249 | if err != nil { 250 | t.Fatal(err) 251 | } 252 | if m.Name != "Gopher" { 253 | t.Errorf("Name = %q; want Gopher", m.Name) 254 | } 255 | rest, err := io.ReadAll(d.Buffered()) 256 | if err != nil { 257 | t.Fatal(err) 258 | } 259 | if g, w := string(rest), " extra "; g != w { 260 | t.Errorf("Remaining = %q; want %q", g, w) 261 | } 262 | } 263 | 264 | func nlines(s string, n int) string { 265 | if n <= 0 { 266 | return "" 267 | } 268 | for i, c := range s { 269 | if c == '\n' { 270 | if n--; n == 0 { 271 | return s[0 : i+1] 272 | } 273 | } 274 | } 275 | return s 276 | } 277 | 278 | func TestRawMessage(t *testing.T) { 279 | var data struct { 280 | X float64 281 | Id RawMessage 282 | Y float32 283 | } 284 | const raw = `["\u0056",null]` 285 | const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}` 286 | err := Unmarshal([]byte(msg), &data) 287 | if err != nil { 288 | t.Fatalf("Unmarshal: %v", err) 289 | } 290 | if string([]byte(data.Id)) != raw { 291 | t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw) 292 | } 293 | b, err := Marshal(&data) 294 | if err != nil { 295 | t.Fatalf("Marshal: %v", err) 296 | } 297 | if string(b) != msg { 298 | t.Fatalf("Marshal: have %#q want %#q", b, msg) 299 | } 300 | } 301 | 302 | func TestNullRawMessage(t *testing.T) { 303 | var data struct { 304 | X float64 305 | Id RawMessage 306 | IdPtr *RawMessage 307 | Y float32 308 | } 309 | const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}` 310 | err := Unmarshal([]byte(msg), &data) 311 | if err != nil { 312 | t.Fatalf("Unmarshal: %v", err) 313 | } 314 | if want, got := "null", string(data.Id); want != got { 315 | t.Fatalf("Raw mismatch: have %q, want %q", got, want) 316 | } 317 | if data.IdPtr != nil { 318 | t.Fatalf("Raw pointer mismatch: have non-nil, want nil") 319 | } 320 | b, err := Marshal(&data) 321 | if err != nil { 322 | t.Fatalf("Marshal: %v", err) 323 | } 324 | if string(b) != msg { 325 | t.Fatalf("Marshal: have %#q want %#q", b, msg) 326 | } 327 | } 328 | 329 | var blockingTests = []string{ 330 | `{"x": 1}`, 331 | `[1, 2, 3]`, 332 | } 333 | 334 | func TestBlocking(t *testing.T) { 335 | for _, enc := range blockingTests { 336 | r, w := net.Pipe() 337 | go w.Write([]byte(enc)) 338 | var val any 339 | 340 | // If Decode reads beyond what w.Write writes above, 341 | // it will block, and the test will deadlock. 342 | if err := NewDecoder(r).Decode(&val); err != nil { 343 | t.Errorf("decoding %s: %v", enc, err) 344 | } 345 | r.Close() 346 | w.Close() 347 | } 348 | } 349 | 350 | type tokenStreamCase struct { 351 | json string 352 | expTokens []any 353 | } 354 | 355 | type decodeThis struct { 356 | v any 357 | } 358 | 359 | var tokenStreamCases = []tokenStreamCase{ 360 | // streaming token cases 361 | {json: `10`, expTokens: []any{float64(10)}}, 362 | {json: ` [10] `, expTokens: []any{ 363 | Delim('['), float64(10), Delim(']')}}, 364 | {json: ` [false,10,"b"] `, expTokens: []any{ 365 | Delim('['), false, float64(10), "b", Delim(']')}}, 366 | {json: `{ "a": 1 }`, expTokens: []any{ 367 | Delim('{'), "a", float64(1), Delim('}')}}, 368 | {json: `{"a": 1, "b":"3"}`, expTokens: []any{ 369 | Delim('{'), "a", float64(1), "b", "3", Delim('}')}}, 370 | {json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{ 371 | Delim('['), 372 | Delim('{'), "a", float64(1), Delim('}'), 373 | Delim('{'), "a", float64(2), Delim('}'), 374 | Delim(']')}}, 375 | {json: `{"obj": {"a": 1}}`, expTokens: []any{ 376 | Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'), 377 | Delim('}')}}, 378 | {json: `{"obj": [{"a": 1}]}`, expTokens: []any{ 379 | Delim('{'), "obj", Delim('['), 380 | Delim('{'), "a", float64(1), Delim('}'), 381 | Delim(']'), Delim('}')}}, 382 | 383 | // streaming tokens with intermittent Decode() 384 | {json: `{ "a": 1 }`, expTokens: []any{ 385 | Delim('{'), "a", 386 | decodeThis{float64(1)}, 387 | Delim('}')}}, 388 | {json: ` [ { "a" : 1 } ] `, expTokens: []any{ 389 | Delim('['), 390 | decodeThis{map[string]any{"a": float64(1)}}, 391 | Delim(']')}}, 392 | {json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{ 393 | Delim('['), 394 | decodeThis{map[string]any{"a": float64(1)}}, 395 | decodeThis{map[string]any{"a": float64(2)}}, 396 | Delim(']')}}, 397 | {json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []any{ 398 | Delim('{'), "obj", Delim('['), 399 | decodeThis{map[string]any{"a": float64(1)}}, 400 | Delim(']'), Delim('}')}}, 401 | 402 | {json: `{"obj": {"a": 1}}`, expTokens: []any{ 403 | Delim('{'), "obj", 404 | decodeThis{map[string]any{"a": float64(1)}}, 405 | Delim('}')}}, 406 | {json: `{"obj": [{"a": 1}]}`, expTokens: []any{ 407 | Delim('{'), "obj", 408 | decodeThis{[]any{ 409 | map[string]any{"a": float64(1)}, 410 | }}, 411 | Delim('}')}}, 412 | {json: ` [{"a": 1} {"a": 2}] `, expTokens: []any{ 413 | Delim('['), 414 | decodeThis{map[string]any{"a": float64(1)}}, 415 | decodeThis{&SyntaxError{"expected comma after array element", 11}}, 416 | }}, 417 | {json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []any{ 418 | Delim('{'), strings.Repeat("a", 513), 419 | decodeThis{&SyntaxError{"expected colon after object key", 518}}, 420 | }}, 421 | {json: `{ "\a" }`, expTokens: []any{ 422 | Delim('{'), 423 | &SyntaxError{"invalid character 'a' in string escape code", 3}, 424 | }}, 425 | {json: ` \a`, expTokens: []any{ 426 | &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1}, 427 | }}, 428 | } 429 | 430 | func TestDecodeInStream(t *testing.T) { 431 | for ci, tcase := range tokenStreamCases { 432 | 433 | dec := NewDecoder(strings.NewReader(tcase.json)) 434 | for i, etk := range tcase.expTokens { 435 | 436 | var tk any 437 | var err error 438 | 439 | if dt, ok := etk.(decodeThis); ok { 440 | etk = dt.v 441 | err = dec.Decode(&tk) 442 | } else { 443 | tk, err = dec.Token() 444 | } 445 | if experr, ok := etk.(error); ok { 446 | if err == nil || !reflect.DeepEqual(err, experr) { 447 | t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err) 448 | } 449 | break 450 | } else if err == io.EOF { 451 | t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json) 452 | break 453 | } else if err != nil { 454 | t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json) 455 | break 456 | } 457 | if !reflect.DeepEqual(tk, etk) { 458 | t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk) 459 | break 460 | } 461 | } 462 | } 463 | } 464 | 465 | // Test from golang.org/issue/11893 466 | func TestHTTPDecoding(t *testing.T) { 467 | const raw = `{ "foo": "bar" }` 468 | 469 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 470 | w.Write([]byte(raw)) 471 | })) 472 | defer ts.Close() 473 | res, err := http.Get(ts.URL) 474 | if err != nil { 475 | log.Fatalf("GET failed: %v", err) 476 | } 477 | defer res.Body.Close() 478 | 479 | foo := struct { 480 | Foo string 481 | }{} 482 | 483 | d := NewDecoder(res.Body) 484 | err = d.Decode(&foo) 485 | if err != nil { 486 | t.Fatalf("Decode: %v", err) 487 | } 488 | if foo.Foo != "bar" { 489 | t.Errorf("decoded %q; want \"bar\"", foo.Foo) 490 | } 491 | 492 | // make sure we get the EOF the second time 493 | err = d.Decode(&foo) 494 | if err != io.EOF { 495 | t.Errorf("err = %v; want io.EOF", err) 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /v5/internal/json/tables.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import "unicode/utf8" 8 | 9 | // safeSet holds the value true if the ASCII character with the given array 10 | // position can be represented inside a JSON string without any further 11 | // escaping. 12 | // 13 | // All values are true except for the ASCII control characters (0-31), the 14 | // double quote ("), and the backslash character ("\"). 15 | var safeSet = [utf8.RuneSelf]bool{ 16 | ' ': true, 17 | '!': true, 18 | '"': false, 19 | '#': true, 20 | '$': true, 21 | '%': true, 22 | '&': true, 23 | '\'': true, 24 | '(': true, 25 | ')': true, 26 | '*': true, 27 | '+': true, 28 | ',': true, 29 | '-': true, 30 | '.': true, 31 | '/': true, 32 | '0': true, 33 | '1': true, 34 | '2': true, 35 | '3': true, 36 | '4': true, 37 | '5': true, 38 | '6': true, 39 | '7': true, 40 | '8': true, 41 | '9': true, 42 | ':': true, 43 | ';': true, 44 | '<': true, 45 | '=': true, 46 | '>': true, 47 | '?': true, 48 | '@': true, 49 | 'A': true, 50 | 'B': true, 51 | 'C': true, 52 | 'D': true, 53 | 'E': true, 54 | 'F': true, 55 | 'G': true, 56 | 'H': true, 57 | 'I': true, 58 | 'J': true, 59 | 'K': true, 60 | 'L': true, 61 | 'M': true, 62 | 'N': true, 63 | 'O': true, 64 | 'P': true, 65 | 'Q': true, 66 | 'R': true, 67 | 'S': true, 68 | 'T': true, 69 | 'U': true, 70 | 'V': true, 71 | 'W': true, 72 | 'X': true, 73 | 'Y': true, 74 | 'Z': true, 75 | '[': true, 76 | '\\': false, 77 | ']': true, 78 | '^': true, 79 | '_': true, 80 | '`': true, 81 | 'a': true, 82 | 'b': true, 83 | 'c': true, 84 | 'd': true, 85 | 'e': true, 86 | 'f': true, 87 | 'g': true, 88 | 'h': true, 89 | 'i': true, 90 | 'j': true, 91 | 'k': true, 92 | 'l': true, 93 | 'm': true, 94 | 'n': true, 95 | 'o': true, 96 | 'p': true, 97 | 'q': true, 98 | 'r': true, 99 | 's': true, 100 | 't': true, 101 | 'u': true, 102 | 'v': true, 103 | 'w': true, 104 | 'x': true, 105 | 'y': true, 106 | 'z': true, 107 | '{': true, 108 | '|': true, 109 | '}': true, 110 | '~': true, 111 | '\u007f': true, 112 | } 113 | 114 | // htmlSafeSet holds the value true if the ASCII character with the given 115 | // array position can be safely represented inside a JSON string, embedded 116 | // inside of HTML