Skip to content

Commit 3da7b27

Browse files
authored
Merge pull request #196 from evanphx/b-null-mistype-panic
Handle null correctly when introduced by replace. Fixes #171
2 parents 850009d + a82b43d commit 3da7b27

File tree

4 files changed

+60
-34
lines changed

4 files changed

+60
-34
lines changed

v5/merge.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
130130
}
131131

132132
if isSyntaxError(patchErr) {
133+
if json.Valid(patchData) {
134+
return patchData, nil
135+
}
136+
133137
return nil, errBadJSONPatch
134138
}
135139

@@ -154,6 +158,10 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
154158
patchErr = unmarshal(patchData, patchAry)
155159

156160
if patchErr != nil {
161+
// Not an array either, a literal is the result directly.
162+
if json.Valid(patchData) {
163+
return patchData, nil
164+
}
157165
return nil, errBadJSONPatch
158166
}
159167

v5/merge_test.go

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package jsonpatch
22

33
import (
44
"fmt"
5-
"strings"
65
"testing"
76
)
87

98
func mergePatch(doc, patch string) string {
109
out, err := MergePatch([]byte(doc), []byte(patch))
1110

1211
if err != nil {
13-
panic(err)
12+
panic(fmt.Sprintf("%s: %s", err, patch))
1413
}
1514

1615
return string(out)
@@ -166,8 +165,8 @@ var rfcTests = []struct {
166165
{target: `{"a":[{"b":"c"}]}`, patch: `{"a":[1]}`, expected: `{"a":[1]}`},
167166
{target: `["a","b"]`, patch: `["c","d"]`, expected: `["c","d"]`},
168167
{target: `{"a":"b"}`, patch: `["c"]`, expected: `["c"]`},
169-
// {target: `{"a":"foo"}`, patch: `null`, expected: `null`},
170-
// {target: `{"a":"foo"}`, patch: `"bar"`, expected: `"bar"`},
168+
{target: `{"a":"foo"}`, patch: `null`, expected: `null`},
169+
{target: `{"a":"foo"}`, patch: `"bar"`, expected: `"bar"`},
171170
{target: `{"e":null}`, patch: `{"a":1}`, expected: `{"a":1,"e":null}`},
172171
{target: `[1,2]`, patch: `{"a":"b","c":null}`, expected: `{"a":"b"}`},
173172
{target: `{}`, patch: `{"a":{"bb":{"ccc":null}}}`, expected: `{"a":{"bb":{}}}`},
@@ -183,33 +182,6 @@ func TestMergePatchRFCCases(t *testing.T) {
183182
}
184183
}
185184

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-
213185
func TestResembleJSONArray(t *testing.T) {
214186
testCases := []struct {
215187
input []byte

v5/patch.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ func (n *lazyNode) intoDoc() (*partialDoc, error) {
254254

255255
err := unmarshal(*n.raw, &n.doc)
256256

257+
if n.doc == nil {
258+
return nil, ErrInvalid
259+
}
260+
257261
if err != nil {
258262
return nil, err
259263
}
@@ -308,6 +312,10 @@ func (n *lazyNode) tryDoc() bool {
308312
return false
309313
}
310314

315+
if n.doc == nil {
316+
return false
317+
}
318+
311319
n.which = eDoc
312320
return true
313321
}
@@ -327,6 +335,18 @@ func (n *lazyNode) tryAry() bool {
327335
return true
328336
}
329337

338+
func (n *lazyNode) isNull() bool {
339+
if n == nil {
340+
return true
341+
}
342+
343+
if n.raw == nil {
344+
return true
345+
}
346+
347+
return bytes.Equal(n.compact(), rawJSONNull)
348+
}
349+
330350
func (n *lazyNode) equal(o *lazyNode) bool {
331351
if n.which == eRaw {
332352
if !n.tryDoc() && !n.tryAry() {
@@ -466,6 +486,10 @@ func (o Operation) From() (string, error) {
466486

467487
func (o Operation) value() *lazyNode {
468488
if obj, ok := o["value"]; ok {
489+
// A `null` gets decoded as a nil RawMessage, so let's fix it up here.
490+
if obj == nil {
491+
return newLazyNode(newRawMessage(rawJSONNull))
492+
}
469493
return newLazyNode(obj)
470494
}
471495

@@ -818,7 +842,10 @@ func ensurePathExists(pd *container, path string, options *ApplyOptions) error {
818842
newNode := newLazyNode(newRawMessage(rawJSONObject))
819843

820844
doc.add(part, newNode, options)
821-
doc, _ = newNode.intoDoc()
845+
doc, err = newNode.intoDoc()
846+
if err != nil {
847+
return err
848+
}
822849
}
823850
} else {
824851
if isArray(*target.raw) {
@@ -1025,12 +1052,14 @@ func (p Patch) test(doc *container, op Operation, options *ApplyOptions) error {
10251052
return errors.Wrapf(err, "error in test for path: '%s'", path)
10261053
}
10271054

1055+
ov := op.value()
1056+
10281057
if val == nil {
1029-
if op.value() == nil || op.value().raw == nil {
1058+
if ov.isNull() {
10301059
return nil
10311060
}
10321061
return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
1033-
} else if op.value() == nil {
1062+
} else if ov.isNull() {
10341063
return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
10351064
}
10361065

v5/patch_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,13 @@ var Cases = []Case{
578578
false,
579579
false,
580580
},
581+
{
582+
`{}`,
583+
`[{"op": "replace", "path": "", "value": null}]`,
584+
`null`,
585+
false,
586+
false,
587+
},
581588
}
582589

583590
type BadCase struct {
@@ -727,6 +734,16 @@ var BadCases = []BadCase{
727734
`[{"op": "copy", "path": "/qux", "from": "/baz"}]`,
728735
false,
729736
},
737+
{
738+
`{ "foo": {"bar": []}}`,
739+
`[{"op": "replace", "path": "/foo/bar", "value": null}, {"op": "add", "path": "/foo/bar/0", "value": "blah"}]`,
740+
false,
741+
},
742+
{
743+
`{}`,
744+
`[{"op": "replace", "path": ""}]`,
745+
true,
746+
},
730747
}
731748

732749
// This is not thread safe, so we cannot run patch tests in parallel.

0 commit comments

Comments
 (0)