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:"oldValue"` 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/wI2L/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/wI2L/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) } }
Output:
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/wI2L/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/wI2L/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/wI2L/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/wI2L/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/wI2L/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 CompareWithoutMarshal ¶
CompareWithoutMarshal is similar to Compare, but it assumes that the given interface values consists only of primitives Go types that are recognized by the json.Unmarshal function, and therefore does not marshal/unmarshal before comparison.