jsonpatch

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2022 License: MIT Imports: 6 Imported by: 1

README

JSON-Patch

ci codecov

jsonpatch is a library which provides functionality for applying RFC6902 JSON patches on JSON.

Documentation

Go-Documentation

Import

// package jsonpatch
import "github.com/ldclabs/json-patch"

Examples

Create and apply a JSON Patch
package main

import (
	"fmt"

	jsonpatch "github.com/ldclabs/json-patch"
)

func main() {
	original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
	patchDoc := []byte(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)

	patch, err := jsonpatch.NewPatch(patchDoc)
	if err != nil {
		panic(err)
	}
	modified, err := patch.Apply(original)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", modified)
	// {"name":"Jane","age":24}
}
Create a JSON Patch from Diff
package main

import (
	"fmt"

	jsonpatch "github.com/ldclabs/json-patch"
)

func main() {
	original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
	target := []byte(`{"name":"Jane","age":24}`)

	patch, err := Diff(original, target, nil)
	if err != nil {
		panic(err)
	}
	patchDoc, err := json.Marshal(patch)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", patchDoc)
	// [{"op":"remove","path":"/height"},{"op":"replace","path":"/name","value":"Jane"}]

	patch, err = Diff(original, target, &jsonpatch.DiffOptions{IDKey: "name"})
	if err != nil {
		panic(err)
	}
	patchDoc, err = json.Marshal(patch)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", patchDoc)
	// [{"op":"replace","path":"","value":{"name":"Jane","age":24}}]
}
Create a Node and apply more Patchs
package main

import (
	"fmt"

	jsonpatch "github.com/ldclabs/json-patch"
)

func main() {
	original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
	node := jsonpatch.NewNode(original)

	patch, err := jsonpatch.NewPatch([]byte(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`))
	if err != nil {
		panic(err)
	}
	err = node.Patch(patch, nil)
	if err != nil {
		panic(err)
	}

	modified, err := node.MarshalJSON()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", modified)
	// {"name":"Jane","age":24}

	patch, err = jsonpatch.NewPatch([]byte(`[
		{"op": "replace", "path": "/age", "value": 25}
	]`))
	if err != nil {
		panic(err)
	}
	err = node.Patch(patch, nil)
	if err != nil {
		panic(err)
	}

	modified, err = node.MarshalJSON()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", modified)
	// {"name":"Jane","age":25}
}
Get value by path
package main

import (
	"fmt"

	jsonpatch "github.com/ldclabs/json-patch"
)

func main() {
	doc := []byte(`{
		"baz": "qux",
		"foo": [ "a", 2, "c" ]
	}`)
	node := jsonpatch.NewNode(doc)
	value, err := node.GetValue("/foo/0", nil)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", string(value))
	// "a"
}
Find children by test operations
package main

import (
	"fmt"

	jsonpatch "github.com/ldclabs/json-patch"
)

func main() {
	doc := []byte(`["root", ["p",
		["span", {"data-type": "text"},
			["span", {"data-type": "leaf"}, "Hello 1"],
			["span", {"data-type": "leaf"}, "Hello 2"],
			["span", {"data-type": "leaf"}, "Hello 3"],
			["span", {"data-type": null}, "Hello 4"]
		]
	]]`)

	node := jsonpatch.NewNode(doc)
	tests := jsonpatch.PVs{
		{"/0", []byte(`"span"`)},
		{"/1/data-type", []byte(`"leaf"`)},
	}

	result, err := node.FindChildren(tests, nil)
	if err != nil {
		panic(err)
	}
	for _, r := range result {
		fmt.Printf("Path: \"%s\", Value: %s\n", r.Path, string(r.Value))
	}
	// Path: "/1/1/2", Value: ["span", {"data-type": "leaf"}, "Hello 1"]
	// Path: "/1/1/3", Value: ["span", {"data-type": "leaf"}, "Hello 2"]
	// Path: "/1/1/4", Value: ["span", {"data-type": "leaf"}, "Hello 3"]
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// SupportNegativeIndices decides whether to support non-standard practice of
	// allowing negative indices to mean indices starting at the end of an array.
	// Default to true.
	SupportNegativeIndices bool = true
	// AccumulatedCopySizeLimit limits the total size increase in bytes caused by
	// "copy" operations in a patch.
	AccumulatedCopySizeLimit int64 = 0
)
View Source
var (
	ErrMissing      = errors.New("missing value")
	ErrInvalid      = errors.New("invalid node detected")
	ErrInvalidIndex = errors.New("invalid index referenced")
)

Functions

func Equal

func Equal(a, b []byte) bool

Equal indicates if 2 JSON documents have the same structural equality.

func GetValueByPath

func GetValueByPath(doc []byte, path string) ([]byte, error)

GetValueByPath returns the value of a given path in a raw encoded JSON document.

Types

type AccumulatedCopySizeError

type AccumulatedCopySizeError struct {
	// contains filtered or unexported fields
}

AccumulatedCopySizeError is an error type returned when the accumulated size increase caused by copy operations in a patch operation has exceeded the limit.

func NewAccumulatedCopySizeError

func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError

NewAccumulatedCopySizeError returns an AccumulatedCopySizeError.

func (*AccumulatedCopySizeError) Error

func (a *AccumulatedCopySizeError) Error() string

Error implements the error interface.

type DiffOptions added in v1.3.0

type DiffOptions struct {
	// IDKey is the name of the key to use as the unique identifier for JSON object
	IDKey string
}

DiffOptions is used to customize the behavior of the Diff function.

type Node

type Node struct {
	// contains filtered or unexported fields
}

Node represents a lazy parsing JSON document.

func NewNode

func NewNode(doc json.RawMessage) *Node

NewNode returns a new Node with the given raw encoded JSON document. a nil or empty raw document is equal to JSON null.

func (*Node) Diff added in v1.2.0

func (n *Node) Diff(target *Node, opts *DiffOptions) (Patch, error)

Diff two JSON nodes and generate a JSON Patch.

func (*Node) Equal added in v1.1.0

func (n *Node) Equal(o *Node) bool

Equal indicates if two JSON Nodes have the same structural equality.

func (*Node) FindChildren

func (n *Node) FindChildren(tests []*PV, options *Options) (result []*PV, err error)

FindChildren returns the children nodes that pass the given test operations in the node.

Example
doc := []byte(`["root", ["p",
		["span", {"data-type": "text"},
			["span", {"data-type": "leaf"}, "Hello 1"],
			["span", {"data-type": "leaf"}, "Hello 2"],
			["span", {"data-type": "leaf"}, "Hello 3"],
			["span", {"data-type": null}, "Hello 4"]
		]
	]]`)

node := NewNode(doc)
tests := PVs{
	{"/0", []byte(`"span"`)},
	{"/1/data-type", []byte(`"leaf"`)},
}

result, err := node.FindChildren(tests, nil)
if err != nil {
	panic(err)
}
for _, r := range result {
	fmt.Printf("Path: \"%s\", Value: %s\n", r.Path, string(r.Value))
}
Output:

Path: "/1/1/2", Value: ["span", {"data-type": "leaf"}, "Hello 1"]
Path: "/1/1/3", Value: ["span", {"data-type": "leaf"}, "Hello 2"]
Path: "/1/1/4", Value: ["span", {"data-type": "leaf"}, "Hello 3"]

func (*Node) GetChild

func (n *Node) GetChild(path string, options *Options) (*Node, error)

GetChild returns the child node of a given path in the node.

func (*Node) GetValue

func (n *Node) GetValue(path string, options *Options) (json.RawMessage, error)

GetValue returns the child node of a given path in the node.

Example
doc := []byte(`{
		"baz": "qux",
		"foo": [ "a", 2, "c" ]
	}`)
node := NewNode(doc)

value, err := node.GetValue("/foo/0", nil)
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", string(value))
Output:

"a"

func (*Node) MarshalJSON

func (n *Node) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*Node) Patch

func (n *Node) Patch(p Patch, options *Options) error

Patch applies the given patch to the node.

Example
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
patchDoc0 := []byte(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
patchDoc1 := []byte(`[
		{"op": "replace", "path": "/age", "value": 25}
	]`)

node := NewNode(original)
patch, err := NewPatch(patchDoc0)
if err != nil {
	panic(err)
}
err = node.Patch(patch, nil)
if err != nil {
	panic(err)
}
modified, err := node.MarshalJSON()
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", modified)
// {"name":"Jane","age":24}

patch, err = NewPatch(patchDoc1)
if err != nil {
	panic(err)
}
err = node.Patch(patch, nil)
if err != nil {
	panic(err)
}
modified, err = node.MarshalJSON()
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", modified)
Output:

{"name":"Jane","age":24}
{"name":"Jane","age":25}

func (*Node) String added in v1.1.1

func (n *Node) String() string

String returns a string representation of the node.

func (*Node) UnmarshalJSON

func (n *Node) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

type Operation

type Operation struct {
	Op    string          `json:"op"`
	Path  string          `json:"path"`
	From  string          `json:"from,omitempty"`
	Value json.RawMessage `json:"value,omitempty"`
}

Operation is a single JSON-Patch step, such as a single 'add' operation.

type Options

type Options struct {
	// SupportNegativeIndices decides whether to support non-standard practice of
	// allowing negative indices to mean indices starting at the end of an array.
	// Default to true.
	SupportNegativeIndices bool
	// AccumulatedCopySizeLimit limits the total size increase in bytes caused by
	// "copy" operations in a patch.
	AccumulatedCopySizeLimit int64
	// AllowMissingPathOnRemove indicates whether to fail "remove" operations when the target path is missing.
	// Default to false.
	AllowMissingPathOnRemove bool
	// EnsurePathExistsOnAdd instructs json-patch to recursively create the missing parts of path on "add" operation.
	// Default to false.
	EnsurePathExistsOnAdd bool
}

Options specifies options for calls to ApplyWithOptions. Use NewOptions to obtain default values for Options.

func NewOptions

func NewOptions() *Options

NewOptions creates a default set of options for calls to ApplyWithOptions.

type PV

type PV struct {
	Path  string          `json:"path"`
	Value json.RawMessage `json:"value"`
}

PV represents a node with a path and a raw encoded JSON value.

type PVs

type PVs []*PV

PVs represents a list of PV.

type Patch

type Patch []Operation

Patch is an ordered collection of Operations.

func Diff added in v1.2.0

func Diff(src, dst []byte, opts *DiffOptions) (Patch, error)

Diff two JSON documents and generate a JSON Patch.

Example
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
target := []byte(`{"name":"Jane","age":24}`)

patch, err := Diff(original, target, nil)
if err != nil {
	panic(err)
}
patchDoc, err := json.Marshal(patch)
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", patchDoc)
// [{"op":"remove","path":"/height"},{"op":"replace","path":"/name","value":"Jane"}]

patch, err = Diff(original, target, &DiffOptions{IDKey: "name"})
if err != nil {
	panic(err)
}
patchDoc, err = json.Marshal(patch)
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", patchDoc)
// [{"op":"replace","path":"","value":{"name":"Jane","age":24}}]
Output:

[{"op":"remove","path":"/height"},{"op":"replace","path":"/name","value":"Jane"}]
[{"op":"replace","path":"","value":{"name":"Jane","age":24}}]

func NewPatch

func NewPatch(doc []byte) (Patch, error)

NewPatch decodes the passed JSON document as an RFC 6902 patch.

func (Patch) Apply

func (p Patch) Apply(doc []byte) ([]byte, error)

Apply mutates a JSON document according to the patch, and returns the new document.

Example
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
patchDoc := []byte(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)

patch, err := NewPatch(patchDoc)
if err != nil {
	panic(err)
}
modified, err := patch.Apply(original)
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", modified)
Output:

{"name":"Jane","age":24}

func (Patch) ApplyWithOptions

func (p Patch) ApplyWithOptions(doc []byte, options *Options) ([]byte, error)

ApplyWithOptions mutates a JSON document according to the patch and the passed in Options. It returns the new document.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL