Skip to content

Commit 15d7247

Browse files
committed
Add examples
1 parent 41dd809 commit 15d7247

File tree

6 files changed

+334
-1
lines changed

6 files changed

+334
-1
lines changed

README.md

+190
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,193 @@
88
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
99

1010
`jsonpatch` is a Go library to create JSON patches ([RFC6902](http://tools.ietf.org/html/rfc6902)) directly from arbitrary Go objects and facilitates the implementation of sophisticated custom (e.g. filtered, validated) patch creation.
11+
12+
## Basic Example
13+
14+
```go
15+
package main
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/snorwin/jsonpatch"
21+
)
22+
23+
type Person struct {
24+
Name string `json:"name"`
25+
Age int `json:"age"`
26+
}
27+
28+
func main() {
29+
original := &Person{
30+
Name: "John Doe",
31+
Age: 42,
32+
}
33+
updated := &Person{
34+
Name: "Jane Doe",
35+
Age: 21,
36+
}
37+
38+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original)
39+
fmt.Println(patch.String())
40+
}
41+
```
42+
```json
43+
[{"op":"replace","path":"/name","value":"Jane Doe"},{"op":"replace","path":"/age","value":21}]
44+
```
45+
46+
## Options
47+
### Filter patches using Predicates
48+
The option `WithPredicate` set a patch `Predicate` which can be used to filter or validate the patch creation.
49+
For each kind (`add`, `remove` and `replace`) of a patch a dedicated filter function can be configured. The
50+
predicate will be checked before a patch is created, or the JSON object is processed further.
51+
52+
#### Example
53+
```go
54+
package main
55+
56+
import (
57+
"fmt"
58+
59+
"github.com/snorwin/jsonpatch"
60+
)
61+
62+
type Job struct {
63+
Position string `json:"position"`
64+
Company string `json:"company"`
65+
Volunteer bool `json:"volunteer"`
66+
}
67+
68+
func main() {
69+
original := []Job{
70+
{Position: "IT Trainer", Company: "Powercoders", Volunteer: true},
71+
{Position: "Software Engineer", Company: "Github"},
72+
}
73+
updated := []Job{
74+
{Position: "Senior IT Trainer", Company: "Powercoders", Volunteer: true},
75+
{Position: "Senior Software Engineer", Company: "Github"},
76+
}
77+
78+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original, jsonpatch.WithPredicate(jsonpatch.Funcs{
79+
ReplaceFunc: func(pointer jsonpatch.JSONPointer, value, _ interface{}) bool {
80+
// only update volunteering jobs
81+
if job, ok := value.(Job); ok {
82+
return job.Volunteer
83+
}
84+
return true
85+
},
86+
}))
87+
fmt.Println(patch.String())
88+
}
89+
```
90+
91+
```json
92+
[{"op":"replace","path":"/0/position","value":"Senior IT Trainer"}]
93+
```
94+
95+
96+
### Create partial patches
97+
The option `WithPrefix` is used to specify a JSON pointer prefix if only a sub part of JSON structure needs to be patched,
98+
but the patch still need to be applied on the entire JSON object.
99+
100+
#### Example
101+
```go
102+
package main
103+
104+
import (
105+
"fmt"
106+
107+
"github.com/snorwin/jsonpatch"
108+
)
109+
110+
type Person struct {
111+
Name string `json:"name"`
112+
Age int `json:"age"`
113+
Jobs []Job `json:"jobs"`
114+
}
115+
116+
type Job struct {
117+
Position string `json:"position"`
118+
Company string `json:"company"`
119+
Volunteer bool `json:"volunteer"`
120+
}
121+
122+
func main() {
123+
original := &Person{
124+
Name: "John Doe",
125+
Age: 42,
126+
Jobs: []Job{{Position: "IT Trainer", Company: "Powercoders"}},
127+
}
128+
updated := []Job{
129+
{Position: "Senior IT Trainer", Company: "Powercoders", Volunteer: true},
130+
{Position: "Software Engineer", Company: "Github"},
131+
}
132+
133+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original.Jobs, jsonpatch.WithPrefix(jsonpatch.ParseJSONPointer("/jobs")))
134+
fmt.Println(patch.String())
135+
}
136+
```
137+
```json
138+
[{"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}}]
139+
```
140+
141+
### Ignore slice order
142+
There are two options to ignore the slice order:
143+
- `IgnoreSliceOrder` will ignore the order of all slices of built-in in types (e.g. `int`, `string`) during the patch creation
144+
and will use instead the value itself in order to match and compare the current and modified JSON.
145+
- `IgnoreSliceOrderWithPattern` allows to specify for which slices the order should be ignored using JSONPointer patterns (e.g. `/jobs`, `/jobs/*`).
146+
Furthermore, the slice order of structs (and pointer of structs) slices can be ignored by specifying a JSON field which should be used
147+
to match the struct values.
148+
149+
> NOTE: Ignoring the slice order only works if the elements (or the values used to match structs) are unique
150+
151+
#### Example
152+
```go
153+
package main
154+
155+
import (
156+
"fmt"
157+
158+
"github.com/snorwin/jsonpatch"
159+
)
160+
161+
type Person struct {
162+
Name string `json:"name"`
163+
Pseudonyms []string `json:"pseudonyms"`
164+
Jobs []Job `json:"jobs"`
165+
}
166+
167+
type Job struct {
168+
Position string `json:"position"`
169+
Company string `json:"company"`
170+
Volunteer bool `json:"volunteer"`
171+
}
172+
173+
func main() {
174+
original := Person{
175+
Name: "John Doe",
176+
Pseudonyms: []string{"Jo", "JayD"},
177+
Jobs: []Job{
178+
{Position: "Software Engineer", Company: "Github"},
179+
{Position: "IT Trainer", Company: "Powercoders"},
180+
},
181+
}
182+
updated := Person{
183+
Name: "John Doe",
184+
Pseudonyms: []string{"Jonny", "Jo"},
185+
Jobs: []Job{
186+
{Position: "IT Trainer", Company: "Powercoders", Volunteer: true},
187+
{Position: "Senior Software Engineer", Company: "Github"},
188+
},
189+
}
190+
191+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original,
192+
jsonpatch.IgnoreSliceOrderWithPattern([]jsonpatch.IgnorePattern{{Pattern: "/*", JSONField: "company"}}),
193+
jsonpatch.IgnoreSliceOrder(),
194+
)
195+
fmt.Println(patch.String())
196+
}
197+
```
198+
```json
199+
[{"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"}]
200+
```

examples/basic_example/main.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/snorwin/jsonpatch"
7+
)
8+
9+
type Person struct {
10+
Name string `json:"name"`
11+
Age int `json:"age"`
12+
}
13+
14+
func main() {
15+
original := &Person{
16+
Name: "John Doe",
17+
Age: 42,
18+
}
19+
updated := &Person{
20+
Name: "Jane Doe",
21+
Age: 21,
22+
}
23+
24+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original)
25+
fmt.Println(patch.String())
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/snorwin/jsonpatch"
7+
)
8+
9+
type Job struct {
10+
Position string `json:"position"`
11+
Company string `json:"company"`
12+
Volunteer bool `json:"volunteer"`
13+
}
14+
15+
func main() {
16+
original := []Job{
17+
{Position: "IT Trainer", Company: "Powercoders", Volunteer: true},
18+
{Position: "Software Engineer", Company: "Github"},
19+
}
20+
updated := []Job{
21+
{Position: "Senior IT Trainer", Company: "Powercoders", Volunteer: true},
22+
{Position: "Senior Software Engineer", Company: "Github"},
23+
}
24+
25+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original, jsonpatch.WithPredicate(jsonpatch.Funcs{
26+
ReplaceFunc: func(pointer jsonpatch.JSONPointer, value, _ interface{}) bool {
27+
// only update volunteering jobs
28+
if job, ok := value.(Job); ok {
29+
return job.Volunteer
30+
}
31+
return true
32+
},
33+
}))
34+
fmt.Println(patch.String())
35+
}

examples/ignore_slice_order/main.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/snorwin/jsonpatch"
7+
)
8+
9+
type Person struct {
10+
Name string `json:"name"`
11+
Pseudonyms []string `json:"pseudonyms"`
12+
Jobs []Job `json:"jobs"`
13+
}
14+
15+
type Job struct {
16+
Position string `json:"position"`
17+
Company string `json:"company"`
18+
Volunteer bool `json:"volunteer"`
19+
}
20+
21+
func main() {
22+
original := Person{
23+
Name: "John Doe",
24+
Pseudonyms: []string{"Jo", "JayD"},
25+
Jobs: []Job{
26+
{Position: "Software Engineer", Company: "Github"},
27+
{Position: "IT Trainer", Company: "Powercoders"},
28+
},
29+
}
30+
updated := Person{
31+
Name: "John Doe",
32+
Pseudonyms: []string{"Jonny", "Jo"},
33+
Jobs: []Job{
34+
{Position: "IT Trainer", Company: "Powercoders", Volunteer: true},
35+
{Position: "Senior Software Engineer", Company: "Github"},
36+
},
37+
}
38+
39+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original,
40+
jsonpatch.IgnoreSliceOrderWithPattern([]jsonpatch.IgnorePattern{{Pattern: "/*", JSONField: "company"}}),
41+
jsonpatch.IgnoreSliceOrder(),
42+
)
43+
fmt.Println(patch.String())
44+
}

examples/partial_patches/main.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/snorwin/jsonpatch"
7+
)
8+
9+
type Person struct {
10+
Name string `json:"name"`
11+
Age int `json:"age"`
12+
Jobs []Job `json:"jobs"`
13+
}
14+
15+
type Job struct {
16+
Position string `json:"position"`
17+
Company string `json:"company"`
18+
Volunteer bool `json:"volunteer"`
19+
}
20+
21+
func main() {
22+
original := &Person{
23+
Name: "John Doe",
24+
Age: 42,
25+
Jobs: []Job{{Position: "IT Trainer", Company: "Powercoders"}},
26+
}
27+
updated := []Job{
28+
{Position: "Senior IT Trainer", Company: "Powercoders", Volunteer: true},
29+
{Position: "Software Engineer", Company: "Github"},
30+
}
31+
32+
patch, _, _ := jsonpatch.CreateJSONPatch(updated, original.Jobs, jsonpatch.WithPrefix(jsonpatch.ParseJSONPointer("/jobs")))
33+
fmt.Println(patch.String())
34+
}

options.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ func WithHandler(handler Handler) Option {
2020
// WithPrefix is used to specify a prefix if only a sub part of JSON structure needs to be patched
2121
func WithPrefix(prefix []string) Option {
2222
return func(w *walker) {
23-
w.prefix = append(w.prefix, prefix...)
23+
if len(prefix) > 0 && prefix[0] == "" {
24+
w.prefix = append(w.prefix, prefix[1:]...)
25+
} else {
26+
w.prefix = append(w.prefix, prefix...)
27+
}
2428
}
2529
}
2630

0 commit comments

Comments
 (0)