...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package gojsonpointer
27
28 import (
29 "errors"
30 "fmt"
31 "reflect"
32 "strconv"
33 "strings"
34 )
35
36 const (
37 const_empty_pointer = ``
38 const_pointer_separator = `/`
39
40 const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator + `"`
41 )
42
43 type implStruct struct {
44 mode string
45
46 inDocument interface{}
47
48 setInValue interface{}
49
50 getOutNode interface{}
51 getOutKind reflect.Kind
52 outError error
53 }
54
55 type JsonPointer struct {
56 referenceTokens []string
57 }
58
59
60 func NewJsonPointer(jsonPointerString string) (p JsonPointer, err error) {
61
62
63 if len(jsonPointerString) == 0 {
64
65 return
66 }
67 if jsonPointerString[0] != '/' {
68 return p, errors.New(const_invalid_start)
69 }
70
71 p.referenceTokens = strings.Split(jsonPointerString[1:], const_pointer_separator)
72 return
73 }
74
75
76 func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
77
78 is := &implStruct{mode: "GET", inDocument: document}
79 p.implementation(is)
80 return is.getOutNode, is.getOutKind, is.outError
81
82 }
83
84
85 func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
86
87 is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
88 p.implementation(is)
89 return document, is.outError
90
91 }
92
93
94 func (p *JsonPointer) Delete(document interface{}) (interface{}, error) {
95 is := &implStruct{mode: "DEL", inDocument: document}
96 p.implementation(is)
97 return document, is.outError
98 }
99
100
101 func (p *JsonPointer) implementation(i *implStruct) {
102
103 kind := reflect.Invalid
104
105
106 if len(p.referenceTokens) == 0 {
107 i.getOutNode = i.inDocument
108 i.outError = nil
109 i.getOutKind = kind
110 i.outError = nil
111 return
112 }
113
114 node := i.inDocument
115
116 previousNodes := make([]interface{}, len(p.referenceTokens))
117 previousTokens := make([]string, len(p.referenceTokens))
118
119 for ti, token := range p.referenceTokens {
120
121 isLastToken := ti == len(p.referenceTokens)-1
122 previousNodes[ti] = node
123 previousTokens[ti] = token
124
125 switch v := node.(type) {
126
127 case map[string]interface{}:
128 decodedToken := decodeReferenceToken(token)
129 if _, ok := v[decodedToken]; ok {
130 node = v[decodedToken]
131 if isLastToken && i.mode == "SET" {
132 v[decodedToken] = i.setInValue
133 } else if isLastToken && i.mode == "DEL" {
134 delete(v, decodedToken)
135 }
136 } else if isLastToken && i.mode == "SET" {
137 v[decodedToken] = i.setInValue
138 } else {
139 i.outError = fmt.Errorf("Object has no key '%s'", decodedToken)
140 i.getOutKind = reflect.Map
141 i.getOutNode = nil
142 return
143 }
144
145 case []interface{}:
146 tokenIndex, err := strconv.Atoi(token)
147 if err != nil {
148 i.outError = fmt.Errorf("Invalid array index '%s'", token)
149 i.getOutKind = reflect.Slice
150 i.getOutNode = nil
151 return
152 }
153 if tokenIndex < 0 || tokenIndex >= len(v) {
154 i.outError = fmt.Errorf("Out of bound array[0,%d] index '%d'", len(v), tokenIndex)
155 i.getOutKind = reflect.Slice
156 i.getOutNode = nil
157 return
158 }
159
160 node = v[tokenIndex]
161 if isLastToken && i.mode == "SET" {
162 v[tokenIndex] = i.setInValue
163 } else if isLastToken && i.mode == "DEL" {
164 v[tokenIndex] = v[len(v)-1]
165 v[len(v)-1] = nil
166 v = v[:len(v)-1]
167 previousNodes[ti-1].(map[string]interface{})[previousTokens[ti-1]] = v
168 }
169
170 default:
171 i.outError = fmt.Errorf("Invalid token reference '%s'", token)
172 i.getOutKind = reflect.ValueOf(node).Kind()
173 i.getOutNode = nil
174 return
175 }
176
177 }
178
179 i.getOutNode = node
180 i.getOutKind = reflect.ValueOf(node).Kind()
181 i.outError = nil
182 }
183
184
185 func (p *JsonPointer) String() string {
186
187 if len(p.referenceTokens) == 0 {
188 return const_empty_pointer
189 }
190
191 pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
192
193 return pointerString
194 }
195
196
197
198
199
200
201 func decodeReferenceToken(token string) string {
202 step1 := strings.Replace(token, `~1`, `/`, -1)
203 step2 := strings.Replace(step1, `~0`, `~`, -1)
204 return step2
205 }
206
207 func encodeReferenceToken(token string) string {
208 step1 := strings.Replace(token, `~`, `~0`, -1)
209 step2 := strings.Replace(step1, `/`, `~1`, -1)
210 return step2
211 }
212
View as plain text