From 7b8895c9258ff24a26f3a7e0ecd46bea33d7a573 Mon Sep 17 00:00:00 2001 From: Evan Phoenix Date: Fri, 12 Jan 2024 09:35:22 -0800 Subject: [PATCH] Use internal/json --- v5/merge.go | 14 +++++++++----- v5/merge_test.go | 21 +++++++++++---------- v5/patch.go | 35 ++++++++++------------------------- 3 files changed, 30 insertions(+), 40 deletions(-) diff --git a/v5/merge.go b/v5/merge.go index 369144e..fe71070 100644 --- a/v5/merge.go +++ b/v5/merge.go @@ -2,11 +2,12 @@ package jsonpatch import ( "bytes" - "encoding/json" "errors" "fmt" "io" "reflect" + + "github.com/evanphx/json-patch/v5/internal/json" ) func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode { @@ -123,7 +124,9 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { docErr := unmarshal(docData, doc) - patch := &partialDoc{} + patch := &partialDoc{ + fastKeys: true, + } patchErr := unmarshal(patchData, patch) @@ -144,6 +147,9 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { } if patchErr == nil && patch.obj == nil { + if json.Valid(patchData) { + return patchData, nil + } return nil, errBadJSONPatch } @@ -262,9 +268,7 @@ func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { } func unmarshal(data []byte, into interface{}) error { - dec := json.NewDecoder(bytes.NewReader(data)) - dec.UseNumber() - return dec.Decode(into) + return json.Unmarshal(data, into) } // createArrayMergePatch will return an array of merge-patch documents capable diff --git a/v5/merge_test.go b/v5/merge_test.go index 0077a71..bc0766d 100644 --- a/v5/merge_test.go +++ b/v5/merge_test.go @@ -5,11 +5,12 @@ import ( "testing" ) -func mergePatch(doc, patch string) string { +func mergePatch(t *testing.T, doc, patch string) string { + t.Helper() out, err := MergePatch([]byte(doc), []byte(patch)) if err != nil { - panic(fmt.Sprintf("%s: %s", err, patch)) + t.Errorf(fmt.Sprintf("%s: %s", err, patch)) } return string(out) @@ -19,7 +20,7 @@ func TestMergePatchReplaceKey(t *testing.T) { doc := `{ "title": "hello" }` pat := `{ "title": "goodbye" }` - res := mergePatch(doc, pat) + res := mergePatch(t, doc, pat) if !compareJSON(pat, res) { t.Fatalf("Key was not replaced") @@ -30,7 +31,7 @@ func TestMergePatchIgnoresOtherValues(t *testing.T) { doc := `{ "title": "hello", "age": 18 }` pat := `{ "title": "goodbye" }` - res := mergePatch(doc, pat) + res := mergePatch(t, doc, pat) exp := `{ "title": "goodbye", "age": 18 }` @@ -43,7 +44,7 @@ func TestMergePatchNilDoc(t *testing.T) { doc := `{ "title": null }` pat := `{ "title": {"foo": "bar"} }` - res := mergePatch(doc, pat) + res := mergePatch(t, doc, pat) exp := `{ "title": {"foo": "bar"} }` @@ -70,7 +71,7 @@ func TestMergePatchNilArray(t *testing.T) { for _, c := range cases { t.Log(c.original) - act := mergePatch(c.original, c.patch) + act := mergePatch(t, c.original, c.patch) if !compareJSON(c.res, act) { t.Errorf("null values not preserved in array") @@ -82,7 +83,7 @@ func TestMergePatchRecursesIntoObjects(t *testing.T) { doc := `{ "person": { "title": "hello", "age": 18 } }` pat := `{ "person": { "title": "goodbye" } }` - res := mergePatch(doc, pat) + res := mergePatch(t, doc, pat) exp := `{ "person": { "title": "goodbye", "age": 18 } }` @@ -111,7 +112,7 @@ func TestMergePatchReplacesNonObjectsWholesale(t *testing.T) { } for _, c := range cases { - act := mergePatch(c.doc, c.pat) + act := mergePatch(t, c.doc, c.pat) if !compareJSON(c.res, act) { t.Errorf("whole object replacement failed") @@ -175,7 +176,7 @@ var rfcTests = []struct { func TestMergePatchRFCCases(t *testing.T) { for i, c := range rfcTests { - out := mergePatch(c.target, c.patch) + out := mergePatch(t, c.target, c.patch) if !compareJSON(out, c.expected) { 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) @@ -621,7 +622,7 @@ func TestMergePatchReplaceKeyNotEscaping(t *testing.T) { pat := `{ "obj": { "title/escaped": "goodbye" } }` exp := `{ "obj": { "title/escaped": "goodbye" } }` - res := mergePatch(doc, pat) + res := mergePatch(t, doc, pat) if !compareJSON(exp, res) { t.Fatalf("Key was not replaced") diff --git a/v5/patch.go b/v5/patch.go index 2cd66a1..6c3c3a6 100644 --- a/v5/patch.go +++ b/v5/patch.go @@ -2,11 +2,11 @@ package jsonpatch import ( "bytes" - "encoding/json" "fmt" "strconv" "strings" + "github.com/evanphx/json-patch/v5/internal/json" "github.com/pkg/errors" ) @@ -59,6 +59,8 @@ type partialDoc struct { self *lazyNode keys []string obj map[string]*lazyNode + + fastKeys bool } type partialArray struct { @@ -133,7 +135,7 @@ func (n *lazyNode) UnmarshalJSON(data []byte) error { } func (n *partialDoc) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer + var buf strings.Builder if _, err := buf.WriteString("{"); err != nil { return nil, err } @@ -164,7 +166,7 @@ func (n *partialDoc) MarshalJSON() ([]byte, error) { if _, err := buf.WriteString("}"); err != nil { return nil, err } - return buf.Bytes(), nil + return []byte(buf.String()), nil } type syntaxError struct { @@ -176,30 +178,13 @@ func (err *syntaxError) Error() string { } func (n *partialDoc) UnmarshalJSON(data []byte) error { - if err := unmarshal(data, &n.obj); err != nil { - return err - } - buffer := bytes.NewBuffer(data) - d := json.NewDecoder(buffer) - if t, err := d.Token(); err != nil { + keys, err := json.UnmarshalWithKeys(data, &n.obj) + if err != nil { return err - } else if t != startObject { - return &syntaxError{fmt.Sprintf("unexpected JSON token in document node: %v", t)} - } - for d.More() { - k, err := d.Token() - if err != nil { - return err - } - key, ok := k.(string) - if !ok { - return &syntaxError{fmt.Sprintf("unexpected JSON token as document node key: %s", k)} - } - if err := skipValue(d); err != nil { - return err - } - n.keys = append(n.keys, key) } + + n.keys = keys + return nil }