jsonpatch

package module
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2024 License: MIT Imports: 8 Imported by: 12

README

jsonpatch

GitHub Action Documentation Test Go Report Card Coverage Status Releases

jsonpatch is a Go library to create JSON patches (RFC6902) directly from arbitrary Go objects and facilitates the implementation of sophisticated custom (e.g. filtered, validated) patch creation.

Basic Example

package main

import (
	"fmt"

	"github.com/snorwin/jsonpatch"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	original := &Person{
		Name: "John Doe",
		Age:  42,
	}
	updated := &Person{
		Name: "Jane Doe",
		Age:  21,
	}

	patch, _ := jsonpatch.CreateJSONPatch(updated, original)
	fmt.Println(patch.String())
}
[{"op":"replace","path":"/name","value":"Jane Doe"},{"op":"replace","path":"/age","value":21}]

Options

Filter patches using Predicates

The option WithPredicate sets a patch Predicate which can be used to filter or validate the patch creation. For each kind of patch (add, remove and replace) a dedicated filter function can be configured. The predicate will be checked before a patch is created, or the JSON object is processed further.

Example
package main

import (
	"fmt"

	"github.com/snorwin/jsonpatch"
)

type Job struct {
	Position  string `json:"position"`
	Company   string `json:"company"`
	Volunteer bool   `json:"volunteer"`
}

func main() {
	original := []Job{
		{Position: "IT Trainer", Company: "Powercoders", Volunteer: true},
		{Position: "Software Engineer", Company: "Github"},
	}
	updated := []Job{
		{Position: "Senior IT Trainer", Company: "Powercoders", Volunteer: true},
		{Position: "Senior Software Engineer", Company: "Github"},
	}

	patch, _ := jsonpatch.CreateJSONPatch(updated, original, jsonpatch.WithPredicate(jsonpatch.Funcs{
		ReplaceFunc: func(pointer jsonpatch.JSONPointer, value, _ interface{}) bool {
			// only update volunteering jobs
			if job, ok := value.(Job); ok {
				return job.Volunteer
			}
			return true
		},
	}))
	fmt.Println(patch.String())
}
[{"op":"replace","path":"/0/position","value":"Senior IT Trainer"}]
Create partial patches

The option WithPrefix is used to specify a JSON pointer prefix if only a sub part of JSON structure needs to be patched, but the patch still need to be applied on the entire JSON object.

Example
package main

import (
	"fmt"

	"github.com/snorwin/jsonpatch"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	Jobs []Job  `json:"jobs"`
}

type Job struct {
	Position  string `json:"position"`
	Company   string `json:"company"`
	Volunteer bool   `json:"volunteer"`
}

func main() {
	original := &Person{
		Name: "John Doe",
		Age:  42,
		Jobs: []Job{{Position: "IT Trainer", Company: "Powercoders"}},
	}
	updated := []Job{
		{Position: "Senior IT Trainer", Company: "Powercoders", Volunteer: true},
		{Position: "Software Engineer", Company: "Github"},
	}
	
	patch, _ := jsonpatch.CreateJSONPatch(updated, original.Jobs, jsonpatch.WithPrefix(jsonpatch.ParseJSONPointer("/jobs")))
	fmt.Println(patch.String())
}
[{"op":"replace","path":"/jobs/0/position","value":"Senior IT Trainer"},{"op":"replace","path":"/jobs/0/volunteer","value":true},{"op":"add","path":"/jobs/1","value":{"position":"Software Engineer","company":"Github","volunteer":false}}]
Ignore slice order

There are two options to ignore the slice order:

  • IgnoreSliceOrder will ignore the order of all slices of built-in types (e.g. int, string) during the patch creation and will instead use the value itself in order to match and compare the current and modified JSON.
  • IgnoreSliceOrderWithPattern allows to specify for which slices the order should be ignored using JSONPointer patterns (e.g. /jobs, /jobs/*). Furthermore, the slice order of structs (and pointer of structs) slices can be ignored by specifying a JSON field which should be used to match the struct values.

NOTE: Ignoring the slice order only works if the elements (or the values used to match structs) are unique

Example
package main

import (
	"fmt"

	"github.com/snorwin/jsonpatch"
)

type Person struct {
	Name       string   `json:"name"`
	Pseudonyms []string `json:"pseudonyms"`
	Jobs       []Job    `json:"jobs"`
}

type Job struct {
	Position  string `json:"position"`
	Company   string `json:"company"`
	Volunteer bool   `json:"volunteer"`
}

func main() {
	original := Person{
		Name:       "John Doe",
		Pseudonyms: []string{"Jo", "JayD"},
		Jobs: []Job{
			{Position: "Software Engineer", Company: "Github"},
			{Position: "IT Trainer", Company: "Powercoders"},
		},
	}
	updated := Person{
		Name:       "John Doe",
		Pseudonyms: []string{"Jonny", "Jo"},
		Jobs: []Job{
			{Position: "IT Trainer", Company: "Powercoders", Volunteer: true},
			{Position: "Senior Software Engineer", Company: "Github"},
		},
	}

	patch, _ := jsonpatch.CreateJSONPatch(updated, original,
		jsonpatch.IgnoreSliceOrderWithPattern([]jsonpatch.IgnorePattern{{Pattern: "/*", JSONField: "company"}}),
		jsonpatch.IgnoreSliceOrder(),
	)
	fmt.Println(patch.String())
}
[{"op":"add","path":"/pseudonyms/2","value":"Jonny"},{"op":"remove","path":"/pseudonyms/1"},{"op":"replace","path":"/jobs/1/volunteer","value":true},{"op":"replace","path":"/jobs/0/position","value":"Senior Software Engineer"}]

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DefaultHandler

type DefaultHandler struct{}

DefaultHandler implements the Handler

func (*DefaultHandler) Add

func (h *DefaultHandler) Add(pointer JSONPointer, value interface{}) []JSONPatch

Add implements Handler

func (*DefaultHandler) Remove

func (h *DefaultHandler) Remove(pointer JSONPointer, _ interface{}) []JSONPatch

Remove implements Handler

func (*DefaultHandler) Replace

func (h *DefaultHandler) Replace(pointer JSONPointer, value, _ interface{}) []JSONPatch

Replace implements Handler

type Funcs

type Funcs struct {
	// Add returns true if the object should not be added in the patch
	AddFunc func(pointer JSONPointer, modified interface{}) bool

	// Remove returns true if the object should not be deleted in the patch
	RemoveFunc func(pointer JSONPointer, current interface{}) bool

	// Replace returns true if the objects should not be updated in the patch - this will stop the recursive processing of those objects
	ReplaceFunc func(pointer JSONPointer, modified, current interface{}) bool
}

Funcs is a function that implements Predicate

func (Funcs) Add

func (p Funcs) Add(pointer JSONPointer, modified interface{}) bool

Add implements Predicate

func (Funcs) Remove

func (p Funcs) Remove(pointer JSONPointer, current interface{}) bool

Remove implements Predicate

func (Funcs) Replace

func (p Funcs) Replace(pointer JSONPointer, modified, current interface{}) bool

Replace implements Predicate

type Handler

type Handler interface {
	// Add creates a JSONPatch with an 'add' operation and appends it to the patch list
	Add(pointer JSONPointer, modified interface{}) []JSONPatch

	// Remove creates a JSONPatch with an 'remove' operation and appends it to the patch list
	Remove(pointer JSONPointer, current interface{}) []JSONPatch

	// Replace creates a JSONPatch with an 'replace' operation and appends it to the patch list
	Replace(pointer JSONPointer, modified, current interface{}) []JSONPatch
}

Handler is the interfaces used by the walker to create patches

type IgnorePattern

type IgnorePattern struct {
	Pattern   string
	JSONField string
}

IgnorePattern specifies a JSONPointer Pattern and a an optional JSONField

type JSONPatch

type JSONPatch struct {
	Operation string      `json:"op"`
	Path      string      `json:"path"`
	Value     interface{} `json:"value,omitempty"`
}

JSONPatch format is specified in RFC 6902

type JSONPatchList added in v1.2.0

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

JSONPatchList is a list of JSONPatch

func CreateJSONPatch

func CreateJSONPatch(modified, current interface{}, options ...Option) (JSONPatchList, error)

CreateJSONPatch compares two JSON data structures and creates a JSONPatch according to RFC 6902

func CreateThreeWayJSONPatch added in v1.5.0

func CreateThreeWayJSONPatch(modified, current, original interface{}, options ...Option) (JSONPatchList, error)

CreateThreeWayJSONPatch compares three JSON data structures and creates a three-way JSONPatch according to RFC 6902

func (JSONPatchList) Empty added in v1.2.0

func (l JSONPatchList) Empty() bool

Empty returns true if the JSONPatchList is empty

func (JSONPatchList) Len added in v1.2.0

func (l JSONPatchList) Len() int

Len returns the length of the JSONPatchList

func (JSONPatchList) List added in v1.2.0

func (l JSONPatchList) List() []JSONPatch

List returns a copy of the underlying JSONPatch slice

func (JSONPatchList) Raw added in v1.2.0

func (l JSONPatchList) Raw() []byte

Raw returns the raw encoded JSON of the JSONPatchList

func (JSONPatchList) String added in v1.2.0

func (l JSONPatchList) String() string

String returns the encoded JSON string of the JSONPatchList

type JSONPointer

type JSONPointer []string

JSONPointer identifies a specific value within a JSON object specified in RFC 6901

func ParseJSONPointer

func ParseJSONPointer(str string) JSONPointer

ParseJSONPointer converts a string into a JSONPointer

func (JSONPointer) Add

func (p JSONPointer) Add(elem string) JSONPointer

Add adds an element to the JSONPointer

func (JSONPointer) Match

func (p JSONPointer) Match(pattern string) bool

Match matches a pattern which is a string JSONPointer which might also contains wildcards

func (JSONPointer) String

func (p JSONPointer) String() string

String returns a string representation of a JSONPointer

type Option

type Option func(r *walker)

Option allow to configure the walker instance

func IgnoreSliceOrder

func IgnoreSliceOrder() Option

IgnoreSliceOrder will ignore the order of all slices of built-in types during the walk and will use instead the value itself in order to compare the current and modified JSON. NOTE: ignoring order only works if the elements in each slice are unique

func IgnoreSliceOrderWithPattern

func IgnoreSliceOrderWithPattern(slices []IgnorePattern) Option

IgnoreSliceOrderWithPattern will ignore the order of slices which paths match the pattern during the walk and will use instead the value in order to compare the current and modified JSON. (For structs (and pointers of structs) the value of the JSON field specified in IgnorePattern is used for comparison.) NOTE: ignoring order only works if the elements in each slice are unique

func WithHandler

func WithHandler(handler Handler) Option

WithHandler set a patch Handler for the walker. This can be used to customize the patch creation

func WithPredicate

func WithPredicate(predicate Predicate) Option

WithPredicate set a patch Predicate for the walker. This can be used to filter or validate the patch creation

func WithPrefix

func WithPrefix(prefix []string) Option

WithPrefix is used to specify a prefix if only a sub part of JSON structure needs to be patched

type Predicate

type Predicate interface {
	// Add returns true if the object should not be added in the patch
	Add(pointer JSONPointer, modified interface{}) bool

	// Remove returns true if the object should not be deleted in the patch
	Remove(pointer JSONPointer, current interface{}) bool

	// Replace returns true if the objects should not be updated in the patch - this will stop the recursive processing of those objects
	Replace(pointer JSONPointer, modified, current interface{}) bool
}

Predicate filters patches

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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