Documentation
¶
Index ¶
Examples ¶
Constants ¶
const ( OperationAdd = "add" OperationReplace = "replace" OperationRemove = "remove" OperationMove = "move" OperationCopy = "copy" OperationTest = "test" )
JSON Patch operation types. These are defined in RFC 6902 section 4. https://datatracker.ietf.org/doc/html/rfc6902#section-4
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Differ ¶
type Differ struct {
// contains filtered or unexported fields
}
A Differ generates JSON Patch (RFC 6902). The zero value is an empty generator ready to use.
func (*Differ) Compare ¶
func (d *Differ) Compare(src, tgt interface{})
Compare computes the differences between src and tgt as a series of JSON Patch operations.
func (*Differ) Patch ¶
Patch returns the list of JSON patch operations generated by the Differ instance. The patch is valid for usage until the next comparison or reset.
type Operation ¶
type Operation struct {
Value interface{} `json:"value,omitempty"`
OldValue interface{} `json:"-"`
Type string `json:"op"`
From string `json:"from,omitempty"`
Path string `json:"path"`
// contains filtered or unexported fields
}
Operation represents a single JSON Patch (RFC6902) operation.
func (Operation) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface.
type Option ¶
type Option func(*Differ)
An Option changes the default behavior of a Differ.
func Equivalent ¶
func Equivalent() Option
Equivalent disables the generation of operations for arrays of equal length and unordered/equal elements.
func Factorize ¶
func Factorize() Option
Factorize enables factorization of operations.
Example ¶
package main
import (
"fmt"
"log"
"github.com/wellplayedgames/jsondiff"
)
func main() {
source := `{"a":[1,2,3],"b":{"foo":"bar"}}`
target := `{"a":[1,2,3],"c":[1,2,3],"d":{"foo":"bar"}}`
patch, err := jsondiff.CompareJSON(
[]byte(source),
[]byte(target),
jsondiff.Factorize(),
)
if err != nil {
log.Fatal(err)
}
for _, op := range patch {
fmt.Printf("%s\n", op)
}
}
Output: {"op":"copy","from":"/a","path":"/c"} {"op":"move","from":"/b","path":"/d"}
func Ignores ¶
Ignores defines the list of values that are ignored by the diff generation, represented as a list of JSON Pointer strings (RFC 6901).
Example ¶
package main
import (
"fmt"
"log"
"github.com/wellplayedgames/jsondiff"
)
func main() {
source := `{"A":"bar","B":"baz","C":"foo"}`
target := `{"A":"rab","B":"baz","D":"foo"}`
patch, err := jsondiff.CompareJSON(
[]byte(source),
[]byte(target),
jsondiff.Ignores("/A", "/C", "/D"),
)
if err != nil {
log.Fatal(err)
}
for _, op := range patch {
fmt.Printf("%s\n", op)
}
}
func InPlaceCompaction ¶
func InPlaceCompaction() Option
InPlaceCompaction instructs to compact the input JSON documents in place; it does not allocate to create a copy, but modify the original byte slice instead. This option has no effect if used alongside SkipCompact.
func Invertible ¶
func Invertible() Option
Invertible enables the generation of an invertible patch, by preceding each remove and replace operation by a test operation that verifies the value at the path that is being removed/replaced. Note that copy operations are not invertible, and as such, using this option disable the usage of copy operation in favor of add operations.
Example ¶
package main
import (
"fmt"
"log"
"github.com/wellplayedgames/jsondiff"
)
func main() {
source := `{"a":"1","b":"2"}`
target := `{"a":"3","c":"4"}`
patch, err := jsondiff.CompareJSON(
[]byte(source),
[]byte(target),
jsondiff.Invertible(),
)
if err != nil {
log.Fatal(err)
}
for _, op := range patch {
fmt.Printf("%s\n", op)
}
}
Output: {"value":"1","op":"test","path":"/a"} {"value":"3","op":"replace","path":"/a"} {"value":"2","op":"test","path":"/b"} {"op":"remove","path":"/b"} {"value":"4","op":"add","path":"/c"}
func MarshalFunc ¶
func MarshalFunc(fn marshalFunc) Option
MarshalFunc allows to define the function/package used to marshal objects to JSON. The prototype of fn must match the one of the encoding/json.Marshal function.
Example ¶
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"github.com/wellplayedgames/jsondiff"
)
type (
Pod struct {
Spec PodSpec `json:"spec,omitempty"`
}
PodSpec struct {
Containers []Container `json:"containers,omitempty"`
Volumes []Volume `json:"volumes,omitempty"`
}
Container struct {
Name string `json:"name"`
Image string `json:"image,omitempty"`
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
}
Volume struct {
Name string `json:"name"`
VolumeSource `json:",inline"`
}
VolumeSource struct {
EmptyDir *EmptyDirVolumeSource `json:"emptyDir,omitempty"`
}
VolumeMount struct {
Name string `json:"name"`
MountPath string `json:"mountPath"`
}
EmptyDirVolumeSource struct {
Medium StorageMedium `json:"medium,omitempty"`
}
StorageMedium string
)
const StorageMediumMemory StorageMedium = "Memory"
func createPod() Pod {
return Pod{
Spec: PodSpec{
Containers: []Container{{
Name: "webserver",
Image: "nginx:latest",
VolumeMounts: []VolumeMount{{
Name: "shared-data",
MountPath: "/usr/share/nginx/html",
}},
}},
Volumes: []Volume{{
Name: "shared-data",
VolumeSource: VolumeSource{
EmptyDir: &EmptyDirVolumeSource{
Medium: StorageMediumMemory,
},
},
}},
},
}
}
func main() {
oldPod := createPod()
newPod := createPod()
newPod.Spec.Containers[0].Name = "nginx"
newPod.Spec.Volumes[0].Name = "data"
patch, err := jsondiff.Compare(
oldPod,
newPod,
jsondiff.MarshalFunc(func(v any) ([]byte, error) {
buf := bytes.Buffer{}
enc := json.NewEncoder(&buf)
err := enc.Encode(v)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}),
)
if err != nil {
log.Fatal(err)
}
for _, op := range patch {
fmt.Printf("%s\n", op)
}
}
Output: {"value":"nginx","op":"replace","path":"/spec/containers/0/name"} {"value":"data","op":"replace","path":"/spec/volumes/0/name"}
func SkipCompact ¶
func SkipCompact() Option
SkipCompact instructs to skip the compaction of the input JSON documents when the Rationalize option is enabled.
func UnmarshalFunc ¶
func UnmarshalFunc(fn unmarshalFunc) Option
UnmarshalFunc allows to define the function/package used to unmarshal objects from JSON. The prototype of fn must match the one of the encoding/json.Unmarshal function.
Example ¶
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"github.com/wellplayedgames/jsondiff"
)
func main() {
source := `{"A":"bar","B":3.14,"C":false}`
target := `{"A":"baz","B":3.14159,"C":true}`
patch, err := jsondiff.CompareJSON(
[]byte(source),
[]byte(target),
jsondiff.UnmarshalFunc(func(b []byte, v any) error {
dec := json.NewDecoder(bytes.NewReader(b))
dec.UseNumber()
return dec.Decode(v)
}),
)
if err != nil {
log.Fatal(err)
}
for _, op := range patch {
fmt.Printf("%s\n", op)
}
}
Output: {"value":"baz","op":"replace","path":"/A"} {"value":3.14159,"op":"replace","path":"/B"} {"value":true,"op":"replace","path":"/C"}
type Patch ¶
type Patch []Operation
Patch represents a series of JSON Patch operations.
func Compare ¶
Compare compares the JSON representations of the given values and returns the differences relative to the former as a list of JSON Patch operations.
Example ¶
package main
import (
"fmt"
"log"
"github.com/wellplayedgames/jsondiff"
)
type (
Pod struct {
Spec PodSpec `json:"spec,omitempty"`
}
PodSpec struct {
Containers []Container `json:"containers,omitempty"`
Volumes []Volume `json:"volumes,omitempty"`
}
Container struct {
Name string `json:"name"`
Image string `json:"image,omitempty"`
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
}
Volume struct {
Name string `json:"name"`
VolumeSource `json:",inline"`
}
VolumeSource struct {
EmptyDir *EmptyDirVolumeSource `json:"emptyDir,omitempty"`
}
VolumeMount struct {
Name string `json:"name"`
MountPath string `json:"mountPath"`
}
EmptyDirVolumeSource struct {
Medium StorageMedium `json:"medium,omitempty"`
}
StorageMedium string
)
const (
StorageMediumDefault StorageMedium = ""
StorageMediumMemory StorageMedium = "Memory"
)
func createPod() Pod {
return Pod{
Spec: PodSpec{
Containers: []Container{{
Name: "webserver",
Image: "nginx:latest",
VolumeMounts: []VolumeMount{{
Name: "shared-data",
MountPath: "/usr/share/nginx/html",
}},
}},
Volumes: []Volume{{
Name: "shared-data",
VolumeSource: VolumeSource{
EmptyDir: &EmptyDirVolumeSource{
Medium: StorageMediumMemory,
},
},
}},
},
}
}
func main() {
oldPod := createPod()
newPod := createPod()
newPod.Spec.Containers[0].Image = "nginx:1.19.5-alpine"
newPod.Spec.Volumes[0].EmptyDir.Medium = StorageMediumDefault
patch, err := jsondiff.Compare(oldPod, newPod)
if err != nil {
log.Fatal(err)
}
for _, op := range patch {
fmt.Printf("%s\n", op)
}
}
Output: {"value":"nginx:1.19.5-alpine","op":"replace","path":"/spec/containers/0/image"} {"op":"remove","path":"/spec/volumes/0/emptyDir/medium"}
func CompareJSON ¶
CompareJSON compares the given JSON documents and returns the differences relative to the former as a list of JSON Patch operations.
Example ¶
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"github.com/wellplayedgames/jsondiff"
)
func main() {
type Phone struct {
Type string `json:"type"`
Number string `json:"number"`
}
type Person struct {
Firstname string `json:"firstName"`
Lastname string `json:"lastName"`
Gender string `json:"gender"`
Age int `json:"age"`
Phones []Phone `json:"phoneNumbers"`
}
source, err := os.ReadFile("testdata/examples/person.json")
if err != nil {
log.Fatal(err)
}
var john Person
if err := json.Unmarshal(source, &john); err != nil {
log.Fatal(err)
}
john.Age = 30
john.Phones = append(john.Phones, Phone{
Type: "mobile",
Number: "209-212-0015",
})
target, err := json.Marshal(john)
if err != nil {
log.Fatal(err)
}
patch, err := jsondiff.CompareJSON(source, target)
if err != nil {
log.Fatal(err)
}
for _, op := range patch {
fmt.Printf("%s\n", op)
}
}
Output: {"value":30,"op":"replace","path":"/age"} {"value":{"number":"209-212-0015","type":"mobile"},"op":"add","path":"/phoneNumbers/-"}
func CompareWithoutMarshals ¶
CompareWithoutMarshals functions similarly to Compare but assumes the interface provided consists only of primitive types valid for the JSON format and therefore does not need marshalunmarshaling.