17 var SupportNegativeIndices bool = true
19 type lazyNode struct {
26 type operation map[string]*json.RawMessage
28 // Patch is an ordered collection of operations.
29 type Patch []operation
31 type partialDoc map[string]*lazyNode
32 type partialArray []*lazyNode
34 type container interface {
35 get(key string) (*lazyNode, error)
36 set(key string, val *lazyNode) error
37 add(key string, val *lazyNode) error
38 remove(key string) error
41 func newLazyNode(raw *json.RawMessage) *lazyNode {
42 return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw}
45 func (n *lazyNode) MarshalJSON() ([]byte, error) {
48 return json.Marshal(n.raw)
50 return json.Marshal(n.doc)
52 return json.Marshal(n.ary)
54 return nil, fmt.Errorf("Unknown type")
58 func (n *lazyNode) UnmarshalJSON(data []byte) error {
59 dest := make(json.RawMessage, len(data))
66 func (n *lazyNode) intoDoc() (*partialDoc, error) {
72 return nil, fmt.Errorf("Unable to unmarshal nil pointer as partial document")
75 err := json.Unmarshal(*n.raw, &n.doc)
85 func (n *lazyNode) intoAry() (*partialArray, error) {
91 return nil, fmt.Errorf("Unable to unmarshal nil pointer as partial array")
94 err := json.Unmarshal(*n.raw, &n.ary)
104 func (n *lazyNode) compact() []byte {
105 buf := &bytes.Buffer{}
111 err := json.Compact(buf, *n.raw)
120 func (n *lazyNode) tryDoc() bool {
125 err := json.Unmarshal(*n.raw, &n.doc)
135 func (n *lazyNode) tryAry() bool {
140 err := json.Unmarshal(*n.raw, &n.ary)
150 func (n *lazyNode) equal(o *lazyNode) bool {
152 if !n.tryDoc() && !n.tryAry() {
157 return bytes.Equal(n.compact(), o.compact())
172 for k, v := range n.doc {
179 if v == nil && ov == nil {
191 if o.which != eAry && !o.tryAry() {
195 if len(n.ary) != len(o.ary) {
199 for idx, val := range n.ary {
200 if !val.equal(o.ary[idx]) {
208 func (o operation) kind() string {
209 if obj, ok := o["op"]; ok && obj != nil {
212 err := json.Unmarshal(*obj, &op)
224 func (o operation) path() string {
225 if obj, ok := o["path"]; ok && obj != nil {
228 err := json.Unmarshal(*obj, &op)
240 func (o operation) from() string {
241 if obj, ok := o["from"]; ok && obj != nil {
244 err := json.Unmarshal(*obj, &op)
256 func (o operation) value() *lazyNode {
257 if obj, ok := o["value"]; ok {
258 return newLazyNode(obj)
264 func isArray(buf []byte) bool {
266 for _, c := range buf {
282 func findObject(pd *container, path string) (container, string) {
285 split := strings.Split(path, "/")
291 parts := split[1 : len(split)-1]
293 key := split[len(split)-1]
297 for _, part := range parts {
299 next, ok := doc.get(decodePatchKey(part))
301 if next == nil || ok != nil {
305 if isArray(*next.raw) {
306 doc, err = next.intoAry()
312 doc, err = next.intoDoc()
320 return doc, decodePatchKey(key)
323 func (d *partialDoc) set(key string, val *lazyNode) error {
328 func (d *partialDoc) add(key string, val *lazyNode) error {
333 func (d *partialDoc) get(key string) (*lazyNode, error) {
334 return (*d)[key], nil
337 func (d *partialDoc) remove(key string) error {
340 return fmt.Errorf("Unable to remove nonexistent key: %s", key)
347 func (d *partialArray) set(key string, val *lazyNode) error {
353 idx, err := strconv.Atoi(key)
363 ary := make([]*lazyNode, sz)
370 return fmt.Errorf("Unable to access invalid index: %d", idx)
379 func (d *partialArray) add(key string, val *lazyNode) error {
385 idx, err := strconv.Atoi(key)
390 ary := make([]*lazyNode, len(*d)+1)
395 return fmt.Errorf("Unable to access invalid index: %d", idx)
398 if SupportNegativeIndices {
400 return fmt.Errorf("Unable to access invalid index: %d", idx)
408 copy(ary[0:idx], cur[0:idx])
410 copy(ary[idx+1:], cur[idx:])
416 func (d *partialArray) get(key string) (*lazyNode, error) {
417 idx, err := strconv.Atoi(key)
424 return nil, fmt.Errorf("Unable to access invalid index: %d", idx)
427 return (*d)[idx], nil
430 func (d *partialArray) remove(key string) error {
431 idx, err := strconv.Atoi(key)
439 return fmt.Errorf("Unable to access invalid index: %d", idx)
442 if SupportNegativeIndices {
444 return fmt.Errorf("Unable to access invalid index: %d", idx)
452 ary := make([]*lazyNode, len(cur)-1)
454 copy(ary[0:idx], cur[0:idx])
455 copy(ary[idx:], cur[idx+1:])
462 func (p Patch) add(doc *container, op operation) error {
465 con, key := findObject(doc, path)
468 return fmt.Errorf("jsonpatch add operation does not apply: doc is missing path: \"%s\"", path)
471 return con.add(key, op.value())
474 func (p Patch) remove(doc *container, op operation) error {
477 con, key := findObject(doc, path)
480 return fmt.Errorf("jsonpatch remove operation does not apply: doc is missing path: \"%s\"", path)
483 return con.remove(key)
486 func (p Patch) replace(doc *container, op operation) error {
489 con, key := findObject(doc, path)
492 return fmt.Errorf("jsonpatch replace operation does not apply: doc is missing path: %s", path)
495 _, ok := con.get(key)
497 return fmt.Errorf("jsonpatch replace operation does not apply: doc is missing key: %s", path)
500 return con.set(key, op.value())
503 func (p Patch) move(doc *container, op operation) error {
506 con, key := findObject(doc, from)
509 return fmt.Errorf("jsonpatch move operation does not apply: doc is missing from path: %s", from)
512 val, err := con.get(key)
517 err = con.remove(key)
524 con, key = findObject(doc, path)
527 return fmt.Errorf("jsonpatch move operation does not apply: doc is missing destination path: %s", path)
530 return con.set(key, val)
533 func (p Patch) test(doc *container, op operation) error {
536 con, key := findObject(doc, path)
539 return fmt.Errorf("jsonpatch test operation does not apply: is missing path: %s", path)
542 val, err := con.get(key)
549 if op.value().raw == nil {
552 return fmt.Errorf("Testing value %s failed", path)
553 } else if op.value() == nil {
554 return fmt.Errorf("Testing value %s failed", path)
557 if val.equal(op.value()) {
561 return fmt.Errorf("Testing value %s failed", path)
564 func (p Patch) copy(doc *container, op operation) error {
567 con, key := findObject(doc, from)
570 return fmt.Errorf("jsonpatch copy operation does not apply: doc is missing from path: %s", from)
573 val, err := con.get(key)
580 con, key = findObject(doc, path)
583 return fmt.Errorf("jsonpatch copy operation does not apply: doc is missing destination path: %s", path)
586 return con.set(key, val)
589 // Equal indicates if 2 JSON documents have the same structural equality.
590 func Equal(a, b []byte) bool {
591 ra := make(json.RawMessage, len(a))
593 la := newLazyNode(&ra)
595 rb := make(json.RawMessage, len(b))
597 lb := newLazyNode(&rb)
602 // DecodePatch decodes the passed JSON document as an RFC 6902 patch.
603 func DecodePatch(buf []byte) (Patch, error) {
606 err := json.Unmarshal(buf, &p)
615 // Apply mutates a JSON document according to the patch, and returns the new
617 func (p Patch) Apply(doc []byte) ([]byte, error) {
618 return p.ApplyIndent(doc, "")
621 // ApplyIndent mutates a JSON document according to the patch, and returns the new
622 // document indented.
623 func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
631 err := json.Unmarshal(doc, pd)
639 for _, op := range p {
644 err = p.remove(&pd, op)
646 err = p.replace(&pd, op)
648 err = p.move(&pd, op)
650 err = p.test(&pd, op)
652 err = p.copy(&pd, op)
654 err = fmt.Errorf("Unexpected kind: %s", op.kind())
663 return json.MarshalIndent(pd, "", indent)
666 return json.Marshal(pd)
669 // From http://tools.ietf.org/html/rfc6901#section-4 :
671 // Evaluation of each reference token begins by decoding any escaped
672 // character sequence. This is performed by first transforming any
673 // occurrence of the sequence '~1' to '/', and then transforming any
674 // occurrence of the sequence '~0' to '~'.
677 rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~")
680 func decodePatchKey(k string) string {
681 return rfc6901Decoder.Replace(k)