cborpatch

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Jun 18, 2022 License: MIT Imports: 7 Imported by: 3

README

CBOR-Patch

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

Documentation

Go-Documentation

Import

// package cborpatch
import "github.com/ldclabs/cbor-patch"

Examples

Create and apply a CBOR Patch
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	original := cborpatch.MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
	fmt.Printf("%x\n", original)
	// a363616765f94e00646e616d65644a6f686e66686569676874fb4009ae147ae147ae
	patchDoc := cborpatch.MustFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)

	patch, err := cborpatch.NewPatch(patchDoc)
	if err != nil {
		panic(err)
	}
	modified, err := patch.Apply(original)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a263616765f94e00646e616d65644a616e65
	fmt.Printf("%s\n", cborpatch.MustToJSON(modified))
	// {"age":24,"name":"Jane"}
}
Create a Node and apply more Patchs
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	original := cborpatch.MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
	fmt.Printf("%x\n", original)
	// a363616765f94e00646e616d65644a6f686e66686569676874fb4009ae147ae147ae
	patchDoc0 := cborpatch.MustFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
	patchDoc1 := cborpatch.MustFromJSON(`[
		{"op": "replace", "path": "/age", "value": 25}
	]`)

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

	patch, err = cborpatch.NewPatch(patchDoc1)
	if err != nil {
		panic(err)
	}
	err = node.Patch(patch, nil)
	if err != nil {
		panic(err)
	}
	modified, err = node.MarshalCBOR()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a263616765f94e40646e616d65644a616e65
	modified, err = node.MarshalJSON()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", string(modified))
	// {"age":25,"name":"Jane"}
}
Get value by path
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
  doc := cborpatch.MustFromJSON(`{
		"baz": "qux",
		"foo": [ "a", 2, "c" ]
	}`)
	node := cborpatch.NewNode(doc)

	value, err := node.GetValue("/foo/0", nil)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", value)
	// 6161
	fmt.Printf("%s\n", cborpatch.MustToJSON(value))
	// "a"
}
Find children by test operations
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
doc := cborpatch.MustFromJSON(`["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 := cborpatch.NewNode(doc)
	tests := PVs{
		{"/0", cborpatch.MustFromJSON(`"span"`)},
		{"/1/data-type", cborpatch.MustFromJSON(`"leaf"`)},
	}

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

	// Output:
	// Path: "/1/1/2", Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2031, JSON: ["span",{"data-type":"leaf"},"Hello 1"]
	// Path: "/1/1/3", Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2032, JSON: ["span",{"data-type":"leaf"},"Hello 2"]
	// Path: "/1/1/4", Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2033, JSON: ["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 (
	ErrTestFailed   = errors.New("test failed")
	ErrMissing      = errors.New("missing value")
	ErrUnknownType  = errors.New("unknown object type")
	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 CBOR documents have the same structural equality.

func FromJSON

func FromJSON(doc []byte, v interface{}) ([]byte, error)

FromJSON converts a JSON-encoded data to a CBOR-encoded data with a optional value as struct container. If v is not nil, it will decode data into v and then encode v to CBOR-encoded data.

func GetValueByPath

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

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

func MustFromJSON

func MustFromJSON(doc string) []byte

MustFromJSON converts a JSON-encoded string to a CBOR-encoded data. It will panic if converting failed.

func MustToJSON

func MustToJSON(doc []byte) string

MustToJSON converts a CBOR-encoded data to a JSON-encoded string. It will panic if converting failed.

func SetCBOR

func SetCBOR(
	marshal func(v interface{}) ([]byte, error),
	unmarshal func(data []byte, v interface{}) error,
)

SetCBOR set the underlying global CBOR Marshal and Unmarshal functions. The default is cbor.CanonicalEncOptions's Marshal and default cbor.Unmarshal.

func init() {
	var EncMode, _ = cbor.CanonicalEncOptions().EncMode()
	var DecMode, _ = cbor.DecOptions{
		DupMapKey:   cbor.DupMapKeyQuiet,
		IndefLength: cbor.IndefLengthForbidden,
	}.DecMode()

	cborpatch.SetCBOR(EncMode.Marshal, DecMode.Unmarshal)
}

func ToJSON

func ToJSON(doc []byte, v interface{}) ([]byte, error)

ToJSON converts a CBOR-encoded data to a JSON-encoded data with a optional value as struct container. If v is not nil, it will decode data into v and then encode v to JSON-encoded data.

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 CBORType

type CBORType uint8

CBORType is the type of a raw encoded CBOR value.

const (
	CBORTypePositiveInt CBORType = 0x00
	CBORTypeNegativeInt CBORType = 0x20
	CBORTypeByteString  CBORType = 0x40
	CBORTypeTextString  CBORType = 0x60
	CBORTypeArray       CBORType = 0x80
	CBORTypeMap         CBORType = 0xa0
	CBORTypeTag         CBORType = 0xc0
	CBORTypePrimitives  CBORType = 0xe0
	CBORTypeInvalid     CBORType = 0xff
)

Predefined CBORTypes.

func ReadCBORType

func ReadCBORType(data []byte) CBORType

ReadCBORType returns the type of a raw encoded CBOR value.

func (CBORType) String

func (t CBORType) String() string

String returns a string representation of CBORType.

type Node

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

Node represents a lazy parsing CBOR document.

func NewNode

func NewNode(doc RawMessage) *Node

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

func (*Node) FindChildren

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

FindChildren returns the children nodes that pass the given tests in the node.

Example
doc := MustFromJSON(`["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", MustFromJSON(`"span"`)},
	{"/1/data-type", MustFromJSON(`"leaf"`)},
}

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

Path: "/1/1/2", Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2031, JSON: ["span",{"data-type":"leaf"},"Hello 1"]
Path: "/1/1/3", Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2032, JSON: ["span",{"data-type":"leaf"},"Hello 2"]
Path: "/1/1/4", Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2033, JSON: ["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) (RawMessage, error)

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

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

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

6161
"a"

func (*Node) MarshalCBOR

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

MarshalCBOR implements the cbor.Marshaler interface.

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 := MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
fmt.Printf("%x\n", original)
// a363616765f94e00646e616d65644a6f686e66686569676874fb4009ae147ae147ae
patchDoc0 := MustFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
patchDoc1 := MustFromJSON(`[
		{"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.MarshalCBOR()
if err != nil {
	panic(err)
}
fmt.Printf("%x\n", modified)
// a263616765f94e00646e616d65644a616e65
modified, err = node.MarshalJSON()
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", string(modified))
// {"age":24,"name":"Jane"}

patch, err = NewPatch(patchDoc1)
if err != nil {
	panic(err)
}
err = node.Patch(patch, nil)
if err != nil {
	panic(err)
}
modified, err = node.MarshalCBOR()
if err != nil {
	panic(err)
}
fmt.Printf("%x\n", modified)
// a263616765f94e40646e616d65644a616e65
modified, err = node.MarshalJSON()
if err != nil {
	panic(err)
}
fmt.Printf("%s\n", string(modified))
// {"age":25,"name":"Jane"}
Output:

a363616765f94e00646e616d65644a6f686e66686569676874fb4009ae147ae147ae
a263616765f94e00646e616d65644a616e65
{"age":24,"name":"Jane"}
a263616765f94e40646e616d65644a616e65
{"age":25,"name":"Jane"}

func (*Node) UnmarshalCBOR

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

UnmarshalCBOR implements the cbor.Unmarshaler interface.

type Operation

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

Operation is a single CBOR-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 cbor-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     `cbor:"path"`
	Value RawMessage `cbor:"value"`
}

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

type PVs added in v1.0.1

type PVs []*PV

PVs represents a list of PV.

type Patch

type Patch []Operation

Patch is an ordered collection of Operations.

func NewPatch added in v1.0.1

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

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

func (Patch) Apply

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

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

Example
original := MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
fmt.Printf("%x\n", original)
// a363616765f94e00646e616d65644a6f686e66686569676874fb4009ae147ae147ae
patchDoc := MustFromJSON(`[
		{"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("%x\n", modified)
// a263616765f94e00646e616d65644a616e65
fmt.Printf("%s\n", MustToJSON(modified))
// {"age":24,"name":"Jane"}
Output:

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

func (Patch) ApplyWithOptions

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

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

type RawMessage

type RawMessage = cbor.RawMessage

RawMessage is a raw encoded CBOR value.

Jump to

Keyboard shortcuts

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