Skip to content

Commit 05890ea

Browse files
authored
Merge pull request #1 from snorwin/escape-json-pointer-seperator
Encoded separators '/' and tilde '~' in JSON pointers
2 parents a82bf12 + 24f0f94 commit 05890ea

File tree

2 files changed

+50
-30
lines changed

2 files changed

+50
-30
lines changed

patch_test.go

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,29 @@ type E struct {
5555
}
5656

5757
type F struct {
58+
Str string `json:"a~bc//2a"`
59+
Int int `json:"a/b"`
60+
Bool bool `json:"x~~e"`
61+
}
62+
63+
type G struct {
5864
A *A `json:"a"`
5965
B *B `json:"b,omitempty"`
6066
C C `json:"c"`
6167
D D `json:"d"`
6268
E E `json:"e"`
63-
}
64-
65-
type G struct {
66-
I interface{} `json:"i"`
69+
F F `json:"f"`
6770
}
6871

6972
type H struct {
7073
Ignored string `json:"_"`
7174
NotIgnored string `json:"notIgnored"`
7275
}
7376

77+
type I struct {
78+
I interface{} `json:"i"`
79+
}
80+
7481
var _ = Describe("JSONPatch", func() {
7582
Context("CreateJsonPatch_pointer_values", func() {
7683
It("pointer", func() {
@@ -237,18 +244,28 @@ var _ = Describe("JSONPatch", func() {
237244
testPatchWithExpected(D{StructSliceWithKey: []C{{Str: "key2", Map: map[string]string{"key": "value"}}, {Str: "key3"}}}, D{StructSliceWithKey: []C{{Str: "key2", Map: map[string]string{"key": "value"}}, {Str: "key3"}}}, D{StructSliceWithKey: []C{{Str: "key2", Map: map[string]string{"key": "value"}}, {Str: "key3"}}}, jsonpatch.IgnoreSliceOrderWithPattern([]jsonpatch.IgnorePattern{{"/structsWithKey", "str"}}))
238245
})
239246
})
247+
Context("CreateJsonPatch_escape_pointer", func() {
248+
It("separator", func() {
249+
// add
250+
testPatch(F{"value2", 2, false}, F{})
251+
// replace
252+
testPatch(F{"value1", 1, true}, F{"value2", 2, false})
253+
// no change
254+
testPatch(F{"value1", 1, true}, F{"value1", 1, true})
255+
})
256+
})
240257
Context("CreateJsonPatch_interface", func() {
241258
It("int", func() {
242259
// replace
243-
testPatch(G{2}, G{3})
260+
testPatch(I{2}, I{3})
244261
// no change
245-
testPatch(G{2}, G{2})
262+
testPatch(I{2}, I{2})
246263
})
247264
It("string", func() {
248265
// replace
249-
testPatch(G{"value1"}, G{"value2"})
266+
testPatch(I{"value1"}, I{"value2"})
250267
// no change
251-
testPatch(G{"value1"}, G{"value1"})
268+
testPatch(I{"value1"}, I{"value1"})
252269
})
253270
})
254271
Context("CreateJsonPatch_ignore", func() {
@@ -302,34 +319,34 @@ var _ = Describe("JSONPatch", func() {
302319
})
303320
It("predicate_add", func() {
304321
// add
305-
testPatchWithExpected(F{B: &B{Bool: true, Str: "str"}}, F{}, F{B: &B{Bool: true, Str: "str"}}, jsonpatch.WithPredicate(predicate))
306-
testPatchWithExpected(F{B: &B{Int: 7, Str: "str"}}, F{}, F{B: &B{Int: 7, Str: "str"}}, jsonpatch.WithPredicate(predicate))
322+
testPatchWithExpected(G{B: &B{Bool: true, Str: "str"}}, G{}, G{B: &B{Bool: true, Str: "str"}}, jsonpatch.WithPredicate(predicate))
323+
testPatchWithExpected(G{B: &B{Int: 7, Str: "str"}}, G{}, G{B: &B{Int: 7, Str: "str"}}, jsonpatch.WithPredicate(predicate))
307324
// don't add
308-
testPatchWithExpected(F{B: &B{Bool: false, Str: "str"}, C: C{Map: map[string]string{"key": "value"}}}, F{}, F{C: C{Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
309-
testPatchWithExpected(F{B: &B{Int: 0, Str: "str"}, C: C{Map: map[string]string{"key": "value"}}}, F{}, F{C: C{Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
325+
testPatchWithExpected(G{B: &B{Bool: false, Str: "str"}, C: C{Map: map[string]string{"key": "value"}}}, G{}, G{C: C{Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
326+
testPatchWithExpected(G{B: &B{Int: 0, Str: "str"}, C: C{Map: map[string]string{"key": "value"}}}, G{}, G{C: C{Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
310327
})
311328
It("predicate_replace", func() {
312329
// replace
313-
testPatchWithExpected(F{C: C{Str: "new", Map: map[string]string{"key": "value"}}}, F{C: C{Str: "old"}}, F{C: C{Str: "new", Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
330+
testPatchWithExpected(G{C: C{Str: "new", Map: map[string]string{"key": "value"}}}, G{C: C{Str: "old"}}, G{C: C{Str: "new", Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
314331
// don't replace
315-
testPatchWithExpected(F{C: C{Str: "new"}}, F{C: C{Str: "old", Map: map[string]string{"key": "value"}}}, F{C: C{Str: "old", Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
332+
testPatchWithExpected(G{C: C{Str: "new"}}, G{C: C{Str: "old", Map: map[string]string{"key": "value"}}}, G{C: C{Str: "old", Map: map[string]string{"key": "value"}}}, jsonpatch.WithPredicate(predicate))
316333
})
317334
It("predicate_remove", func() {
318335
// remove
319-
testPatchWithExpected(F{}, F{B: &B{Str: "remove me"}}, F{B: nil}, jsonpatch.WithPredicate(predicate))
336+
testPatchWithExpected(G{}, G{B: &B{Str: "remove me"}}, G{B: nil}, jsonpatch.WithPredicate(predicate))
320337
// don't remove
321-
testPatchWithExpected(F{}, F{B: &B{Str: "don't remove me"}}, F{B: &B{Str: "don't remove me"}}, jsonpatch.WithPredicate(predicate))
338+
testPatchWithExpected(G{}, G{B: &B{Str: "don't remove me"}}, G{B: &B{Str: "don't remove me"}}, jsonpatch.WithPredicate(predicate))
322339
})
323340
})
324341
Context("CreateJsonPatch_with_prefix", func() {
325342
It("empty prefix", func() {
326-
testPatchWithExpected(F{B: &B{Bool: true, Str: "str"}}, F{}, F{B: &B{Bool: true, Str: "str"}}, jsonpatch.WithPrefix([]string{""}))
343+
testPatchWithExpected(G{B: &B{Bool: true, Str: "str"}}, G{}, G{B: &B{Bool: true, Str: "str"}}, jsonpatch.WithPrefix([]string{""}))
327344
})
328345
It("pointer prefix", func() {
329346
prefix := "/a/ptr"
330-
modified := F{A: &A{B: &B{Bool: true, Str: "str"}}}
331-
current := F{A: &A{}}
332-
expected := F{A: &A{B: &B{Bool: true, Str: "str"}}}
347+
modified := G{A: &A{B: &B{Bool: true, Str: "str"}}}
348+
current := G{A: &A{}}
349+
expected := G{A: &A{B: &B{Bool: true, Str: "str"}}}
333350

334351
currentJSON, err := json.Marshal(current)
335352
Ω(err).ShouldNot(HaveOccurred())
@@ -350,9 +367,9 @@ var _ = Describe("JSONPatch", func() {
350367
})
351368
It("string prefix", func() {
352369
prefix := []string{"b"}
353-
modified := F{B: &B{Bool: true, Str: "str"}}
354-
current := F{}
355-
expected := F{B: &B{Bool: true, Str: "str"}}
370+
modified := G{B: &B{Bool: true, Str: "str"}}
371+
current := G{}
372+
expected := G{B: &B{Bool: true, Str: "str"}}
356373

357374
currentJSON, err := json.Marshal(current)
358375
Ω(err).ShouldNot(HaveOccurred())
@@ -378,15 +395,15 @@ var _ = Describe("JSONPatch", func() {
378395
Ω(err).Should(HaveOccurred())
379396
})
380397
It("not matching interface types", func() {
381-
_, err := jsonpatch.CreateJSONPatch(G{1}, G{"str"})
398+
_, err := jsonpatch.CreateJSONPatch(I{1}, I{"str"})
382399
Ω(err).Should(HaveOccurred())
383400
})
384401
It("invalid map (map[string]int)", func() {
385-
_, err := jsonpatch.CreateJSONPatch(G{map[string]int{"key": 2}}, G{map[string]int{"key": 3}})
402+
_, err := jsonpatch.CreateJSONPatch(I{map[string]int{"key": 2}}, I{map[string]int{"key": 3}})
386403
Ω(err).Should(HaveOccurred())
387404
})
388405
It("invalid map (map[int]string)", func() {
389-
_, err := jsonpatch.CreateJSONPatch(G{map[int]string{1: "value"}}, G{map[int]string{2: "value"}})
406+
_, err := jsonpatch.CreateJSONPatch(I{map[int]string{1: "value"}}, I{map[int]string{2: "value"}})
390407
Ω(err).Should(HaveOccurred())
391408
})
392409
It("ignore slice order failed (duplicated key)", func() {
@@ -398,15 +415,15 @@ var _ = Describe("JSONPatch", func() {
398415
})
399416
Context("CreateJsonPatch_fuzzy", func() {
400417
var (
401-
current F
402-
modified F
418+
current G
419+
modified G
403420
)
404421
BeforeEach(func() {
405-
current = F{}
422+
current = G{}
406423
err := faker.FakeData(&current)
407424
Ω(err).ShouldNot(HaveOccurred())
408425

409-
modified = F{}
426+
modified = G{}
410427
err = faker.FakeData(&modified)
411428
Ω(err).ShouldNot(HaveOccurred())
412429
})

pointer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
const (
88
separator = "/"
99
wildcard = "*"
10+
tilde = "~"
1011
)
1112

1213
// JSONPointer identifies a specific value within a JSON object specified in RFC 6901
@@ -24,6 +25,8 @@ func (p JSONPointer) String() string {
2425

2526
// Add adds an element to the JSONPointer
2627
func (p JSONPointer) Add(elem string) JSONPointer {
28+
elem = strings.ReplaceAll(elem, tilde, "~0")
29+
elem = strings.ReplaceAll(elem, separator, "~1")
2730
return append(p, elem)
2831
}
2932

0 commit comments

Comments
 (0)