1
2
3
4
5
6
7
8
9
10 package jsonpointer
11
12 import (
13 "fmt"
14 "net/url"
15 "strconv"
16 "strings"
17 )
18
19 const defaultPointerAllocationSize = 32
20
21
22
23
24
25 func Parse(str string) (Pointer, error) {
26
27 if len(str) == 0 || str == "#" {
28 return Pointer{}, nil
29 } else if str[0] == '/' {
30 return parse(str)
31 }
32
33 u, err := url.Parse(str)
34 if err != nil {
35 return nil, err
36 }
37 return parse(u.Fragment)
38 }
39
40
41
42 func (p Pointer) IsEmpty() bool {
43 return len(p) == 0
44 }
45
46
47 func (p Pointer) Head() *string {
48 if len(p) == 0 {
49 return nil
50 }
51 return &p[0]
52 }
53
54
55 func (p Pointer) Tail() Pointer {
56 return Pointer(p[1:])
57 }
58
59
60
61
62
63
64
65
66 func parse(str string) (Pointer, error) {
67 if len(str) == 0 {
68 return Pointer{}, nil
69 }
70
71 if str[0] != '/' {
72 return nil, fmt.Errorf("non-empty references must begin with a '/' character")
73 }
74 str = str[1:]
75
76 toks := strings.Split(str, separator)
77 for i, t := range toks {
78 toks[i] = unescapeToken(t)
79 }
80 return Pointer(toks), nil
81 }
82
83
84 type Pointer []string
85
86
87
88 func NewPointer() Pointer {
89 return make([]string, 0, defaultPointerAllocationSize)
90 }
91
92
93
94 func (p Pointer) String() (str string) {
95 for _, tok := range p {
96 str += "/" + escapeToken(tok)
97 }
98 return
99 }
100
101
102
103
104
105
106 func (p Pointer) Eval(data interface{}) (result interface{}, err error) {
107 result = data
108 for _, tok := range p {
109 if result, err = p.evalToken(tok, result); err != nil {
110 return nil, err
111 }
112 }
113 return
114 }
115
116
117
118 func (p Pointer) Descendant(path string) (Pointer, error) {
119 if !strings.HasPrefix(path, "/") {
120 path = "/" + path
121 }
122 dpath, err := parse(path)
123 if err != nil {
124 return p, err
125 }
126
127 if p.String() == "/" {
128 return dpath, nil
129 }
130
131 return append(p, dpath...), nil
132 }
133
134
135
136
137
138 func (p Pointer) RawDescendant(path ...string) Pointer {
139 return append(p, path...)
140 }
141
142
143
144
145
146
147
148
149
150
151
152 func (p Pointer) evalToken(tok string, data interface{}) (interface{}, error) {
153 switch ch := data.(type) {
154 case map[string]interface{}:
155 return ch[tok], nil
156 case []interface{}:
157 i, err := strconv.Atoi(tok)
158 if err != nil {
159 return nil, fmt.Errorf("invalid array index: %s", tok)
160 }
161 if i >= len(ch) {
162 return nil, fmt.Errorf("index %d exceeds array length of %d", i, len(ch))
163 }
164 return ch[i], nil
165 default:
166 return nil, fmt.Errorf("invalid JSON pointer: %s", p.String())
167 }
168 }
169
170 const (
171 separator = "/"
172 escapedSeparator = "~1"
173 tilde = "~"
174 escapedTilde = "~0"
175 )
176
177 func unescapeToken(tok string) string {
178 tok = strings.Replace(tok, escapedSeparator, separator, -1)
179 return strings.Replace(tok, escapedTilde, tilde, -1)
180 }
181
182 func escapeToken(tok string) string {
183 tok = strings.Replace(tok, tilde, escapedTilde, -1)
184 return strings.Replace(tok, separator, escapedSeparator, -1)
185 }
186
View as plain text