mirror of https://github.com/k3s-io/k3s
Merge pull request #26968 from jimmidyson/json-patch-lib-upgrade
Automatic merge from submit-queue godep bump: github.com/evanphx/json-patch Fixes #26890pull/6/head
commit
59f918cae0
|
@ -754,7 +754,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/evanphx/json-patch",
|
"ImportPath": "github.com/evanphx/json-patch",
|
||||||
"Rev": "7dd4489c2eb6073e5a9d7746c3274c5b5f0387df"
|
"Rev": "465937c80b3c07a7c7ad20cc934898646a91c1de"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
## JSON-Patch
|
## JSON-Patch
|
||||||
|
|
||||||
Provides the abiilty to modify and test a JSON according to a
|
Provides the ability to modify and test a JSON according to a
|
||||||
[RFC6902 JSON patch](http://tools.ietf.org/html/rfc6902) and [RFC7386 JSON Merge Patch](https://tools.ietf.org/html/rfc7386).
|
[RFC6902 JSON patch](http://tools.ietf.org/html/rfc6902) and [RFC7396 JSON Merge Patch](https://tools.ietf.org/html/rfc7396).
|
||||||
|
|
||||||
*Version*: **1.0**
|
*Version*: **1.0**
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/evanphx/json-patch?status.svg)](http://godoc.org/github.com/evanphx/json-patch)
|
[![GoDoc](https://godoc.org/github.com/evanphx/json-patch?status.svg)](http://godoc.org/github.com/evanphx/json-patch)
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/evanphx/json-patch.svg?branch=RFC7386)](https://travis-ci.org/evanphx/json-patch)
|
[![Build Status](https://travis-ci.org/evanphx/json-patch.svg?branch=master)](https://travis-ci.org/evanphx/json-patch)
|
||||||
|
|
||||||
### API Usage
|
### API Usage
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func merge(cur, patch *lazyNode) *lazyNode {
|
func merge(cur, patch *lazyNode) *lazyNode {
|
||||||
|
@ -27,6 +28,7 @@ func merge(cur, patch *lazyNode) *lazyNode {
|
||||||
|
|
||||||
func mergeDocs(doc, patch *partialDoc) {
|
func mergeDocs(doc, patch *partialDoc) {
|
||||||
for k, v := range *patch {
|
for k, v := range *patch {
|
||||||
|
k := decodePatchKey(k)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
delete(*doc, k)
|
delete(*doc, k)
|
||||||
} else {
|
} else {
|
||||||
|
@ -69,7 +71,7 @@ func pruneDocNulls(doc *partialDoc) *partialDoc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func pruneAryNulls(ary *partialArray) *partialArray {
|
func pruneAryNulls(ary *partialArray) *partialArray {
|
||||||
var newAry []*lazyNode
|
newAry := []*lazyNode{}
|
||||||
|
|
||||||
for _, v := range *ary {
|
for _, v := range *ary {
|
||||||
if v != nil {
|
if v != nil {
|
||||||
|
@ -218,6 +220,9 @@ func matchesValue(av, bv interface{}) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
case []interface{}:
|
||||||
|
bt := bv.([]interface{})
|
||||||
|
return matchesArray(at, bt)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -226,15 +231,16 @@ func matchesValue(av, bv interface{}) bool {
|
||||||
func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) {
|
func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) {
|
||||||
into := map[string]interface{}{}
|
into := map[string]interface{}{}
|
||||||
for key, bv := range b {
|
for key, bv := range b {
|
||||||
|
escapedKey := encodePatchKey(key)
|
||||||
av, ok := a[key]
|
av, ok := a[key]
|
||||||
// value was added
|
// value was added
|
||||||
if !ok {
|
if !ok {
|
||||||
into[key] = bv
|
into[escapedKey] = bv
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If types have changed, replace completely
|
// If types have changed, replace completely
|
||||||
if reflect.TypeOf(av) != reflect.TypeOf(bv) {
|
if reflect.TypeOf(av) != reflect.TypeOf(bv) {
|
||||||
into[key] = bv
|
into[escapedKey] = bv
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Types are the same, compare values
|
// Types are the same, compare values
|
||||||
|
@ -247,23 +253,23 @@ func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(dst) > 0 {
|
if len(dst) > 0 {
|
||||||
into[key] = dst
|
into[escapedKey] = dst
|
||||||
}
|
}
|
||||||
case string, float64, bool:
|
case string, float64, bool:
|
||||||
if !matchesValue(av, bv) {
|
if !matchesValue(av, bv) {
|
||||||
into[key] = bv
|
into[escapedKey] = bv
|
||||||
}
|
}
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
bt := bv.([]interface{})
|
bt := bv.([]interface{})
|
||||||
if !matchesArray(at, bt) {
|
if !matchesArray(at, bt) {
|
||||||
into[key] = bv
|
into[escapedKey] = bv
|
||||||
}
|
}
|
||||||
case nil:
|
case nil:
|
||||||
switch bv.(type) {
|
switch bv.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
// Both nil, fine.
|
// Both nil, fine.
|
||||||
default:
|
default:
|
||||||
into[key] = bv
|
into[escapedKey] = bv
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unknown type:%T in key %s", av, key))
|
panic(fmt.Sprintf("Unknown type:%T in key %s", av, key))
|
||||||
|
@ -278,3 +284,23 @@ func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) {
|
||||||
}
|
}
|
||||||
return into, nil
|
return into, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// From http://tools.ietf.org/html/rfc6901#section-4 :
|
||||||
|
//
|
||||||
|
// Evaluation of each reference token begins by decoding any escaped
|
||||||
|
// character sequence. This is performed by first transforming any
|
||||||
|
// occurrence of the sequence '~1' to '/', and then transforming any
|
||||||
|
// occurrence of the sequence '~0' to '~'.
|
||||||
|
|
||||||
|
var (
|
||||||
|
rfc6901Encoder = strings.NewReplacer("~", "~0", "/", "~1")
|
||||||
|
rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~")
|
||||||
|
)
|
||||||
|
|
||||||
|
func decodePatchKey(k string) string {
|
||||||
|
return rfc6901Decoder.Replace(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodePatchKey(k string) string {
|
||||||
|
return rfc6901Encoder.Replace(k)
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ type partialArray []*lazyNode
|
||||||
type container interface {
|
type container interface {
|
||||||
get(key string) (*lazyNode, error)
|
get(key string) (*lazyNode, error)
|
||||||
set(key string, val *lazyNode) error
|
set(key string, val *lazyNode) error
|
||||||
|
add(key string, val *lazyNode) error
|
||||||
remove(key string) error
|
remove(key string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ func newLazyNode(raw *json.RawMessage) *lazyNode {
|
||||||
func (n *lazyNode) MarshalJSON() ([]byte, error) {
|
func (n *lazyNode) MarshalJSON() ([]byte, error) {
|
||||||
switch n.which {
|
switch n.which {
|
||||||
case eRaw:
|
case eRaw:
|
||||||
return *n.raw, nil
|
return json.Marshal(n.raw)
|
||||||
case eDoc:
|
case eDoc:
|
||||||
return json.Marshal(n.doc)
|
return json.Marshal(n.doc)
|
||||||
case eAry:
|
case eAry:
|
||||||
|
@ -269,7 +270,7 @@ func findObject(pd *partialDoc, path string) (container, string) {
|
||||||
|
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
|
|
||||||
next, ok := doc.get(part)
|
next, ok := doc.get(decodePatchKey(part))
|
||||||
|
|
||||||
if next == nil || ok != nil {
|
if next == nil || ok != nil {
|
||||||
return nil, ""
|
return nil, ""
|
||||||
|
@ -290,7 +291,7 @@ func findObject(pd *partialDoc, path string) (container, string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return doc, key
|
return doc, decodePatchKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *partialDoc) set(key string, val *lazyNode) error {
|
func (d *partialDoc) set(key string, val *lazyNode) error {
|
||||||
|
@ -298,11 +299,21 @@ func (d *partialDoc) set(key string, val *lazyNode) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *partialDoc) add(key string, val *lazyNode) error {
|
||||||
|
(*d)[key] = val
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *partialDoc) get(key string) (*lazyNode, error) {
|
func (d *partialDoc) get(key string) (*lazyNode, error) {
|
||||||
return (*d)[key], nil
|
return (*d)[key], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *partialDoc) remove(key string) error {
|
func (d *partialDoc) remove(key string) error {
|
||||||
|
_, ok := (*d)[key]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Unable to remove nonexistant key: %s", key)
|
||||||
|
}
|
||||||
|
|
||||||
delete(*d, key)
|
delete(*d, key)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -314,7 +325,38 @@ func (d *partialArray) set(key string, val *lazyNode) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
idx, err := strconv.Atoi(key)
|
idx, err := strconv.Atoi(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sz := len(*d)
|
||||||
|
if idx+1 > sz {
|
||||||
|
sz = idx + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ary := make([]*lazyNode, sz)
|
||||||
|
|
||||||
|
cur := *d
|
||||||
|
|
||||||
|
copy(ary, cur)
|
||||||
|
|
||||||
|
if idx >= len(ary) {
|
||||||
|
fmt.Printf("huh?: %#v[%d] %s, %s\n", ary, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
ary[idx] = val
|
||||||
|
|
||||||
|
*d = ary
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *partialArray) add(key string, val *lazyNode) error {
|
||||||
|
if key == "-" {
|
||||||
|
*d = append(*d, val)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := strconv.Atoi(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -338,18 +380,25 @@ func (d *partialArray) get(key string) (*lazyNode, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if idx >= len(*d) {
|
||||||
|
return nil, fmt.Errorf("Unable to access invalid index: %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
return (*d)[idx], nil
|
return (*d)[idx], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *partialArray) remove(key string) error {
|
func (d *partialArray) remove(key string) error {
|
||||||
idx, err := strconv.Atoi(key)
|
idx, err := strconv.Atoi(key)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cur := *d
|
cur := *d
|
||||||
|
|
||||||
|
if idx >= len(cur) {
|
||||||
|
return fmt.Errorf("Unable to remove invalid index: %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
ary := make([]*lazyNode, len(cur)-1)
|
ary := make([]*lazyNode, len(cur)-1)
|
||||||
|
|
||||||
copy(ary[0:idx], cur[0:idx])
|
copy(ary[0:idx], cur[0:idx])
|
||||||
|
@ -366,12 +415,10 @@ func (p Patch) add(doc *partialDoc, op operation) error {
|
||||||
con, key := findObject(doc, path)
|
con, key := findObject(doc, path)
|
||||||
|
|
||||||
if con == nil {
|
if con == nil {
|
||||||
return fmt.Errorf("Missing container: %s", path)
|
return fmt.Errorf("jsonpatch add operation does not apply: doc is missing path: %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
con.set(key, op.value())
|
return con.add(key, op.value())
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Patch) remove(doc *partialDoc, op operation) error {
|
func (p Patch) remove(doc *partialDoc, op operation) error {
|
||||||
|
@ -379,6 +426,10 @@ func (p Patch) remove(doc *partialDoc, op operation) error {
|
||||||
|
|
||||||
con, key := findObject(doc, path)
|
con, key := findObject(doc, path)
|
||||||
|
|
||||||
|
if con == nil {
|
||||||
|
return fmt.Errorf("jsonpatch remove operation does not apply: doc is missing path: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
return con.remove(key)
|
return con.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,9 +438,11 @@ func (p Patch) replace(doc *partialDoc, op operation) error {
|
||||||
|
|
||||||
con, key := findObject(doc, path)
|
con, key := findObject(doc, path)
|
||||||
|
|
||||||
con.set(key, op.value())
|
if con == nil {
|
||||||
|
return fmt.Errorf("jsonpatch replace operation does not apply: doc is missing path: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return con.set(key, op.value())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Patch) move(doc *partialDoc, op operation) error {
|
func (p Patch) move(doc *partialDoc, op operation) error {
|
||||||
|
@ -397,21 +450,29 @@ func (p Patch) move(doc *partialDoc, op operation) error {
|
||||||
|
|
||||||
con, key := findObject(doc, from)
|
con, key := findObject(doc, from)
|
||||||
|
|
||||||
val, err := con.get(key)
|
if con == nil {
|
||||||
|
return fmt.Errorf("jsonpatch move operation does not apply: doc is missing from path: %s", from)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := con.get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
con.remove(key)
|
err = con.remove(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
path := op.path()
|
path := op.path()
|
||||||
|
|
||||||
con, key = findObject(doc, path)
|
con, key = findObject(doc, path)
|
||||||
|
|
||||||
con.set(key, val)
|
if con == nil {
|
||||||
|
return fmt.Errorf("jsonpatch move operation does not apply: doc is missing destination path: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return con.set(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Patch) test(doc *partialDoc, op operation) error {
|
func (p Patch) test(doc *partialDoc, op operation) error {
|
||||||
|
@ -419,12 +480,24 @@ func (p Patch) test(doc *partialDoc, op operation) error {
|
||||||
|
|
||||||
con, key := findObject(doc, path)
|
con, key := findObject(doc, path)
|
||||||
|
|
||||||
|
if con == nil {
|
||||||
|
return fmt.Errorf("jsonpatch test operation does not apply: is missing path: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
val, err := con.get(key)
|
val, err := con.get(key)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val == nil {
|
||||||
|
if op.value().raw == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Testing value %s failed", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if val.equal(op.value()) {
|
if val.equal(op.value()) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -461,6 +534,12 @@ func DecodePatch(buf []byte) (Patch, error) {
|
||||||
// Apply mutates a JSON document according to the patch, and returns the new
|
// Apply mutates a JSON document according to the patch, and returns the new
|
||||||
// document.
|
// document.
|
||||||
func (p Patch) Apply(doc []byte) ([]byte, error) {
|
func (p Patch) Apply(doc []byte) ([]byte, error) {
|
||||||
|
return p.ApplyIndent(doc, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyIndent mutates a JSON document according to the patch, and returns the new
|
||||||
|
// document indented.
|
||||||
|
func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
|
||||||
pd := &partialDoc{}
|
pd := &partialDoc{}
|
||||||
|
|
||||||
err := json.Unmarshal(doc, pd)
|
err := json.Unmarshal(doc, pd)
|
||||||
|
@ -492,5 +571,9 @@ func (p Patch) Apply(doc []byte) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if indent != "" {
|
||||||
|
return json.MarshalIndent(pd, "", indent)
|
||||||
|
}
|
||||||
|
|
||||||
return json.Marshal(pd)
|
return json.Marshal(pd)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue