-
Notifications
You must be signed in to change notification settings - Fork 1
/
node.go
313 lines (265 loc) · 6.11 KB
/
node.go
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
package lzjson
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"regexp"
"strings"
)
// reNumber is the regular expression to match
// any JSON number values
var reNum = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?$`)
// isNumJSON test a string and see if it match the
// JSON definition of number
func isNumJSON(b []byte) bool {
return reNum.Match(b)
}
// Node is an interface for all JSON nodes
type Node interface {
// Unmarshal parses the JSON node data into variable v
Unmarshal(v interface{}) error
// UnmarshalJSON implements json.Unmarshaler
UnmarshalJSON(b []byte) error
// Raw returns the raw JSON string in []byte
Raw() []byte
// Type returns the Type of the containing JSON value
Type() Type
// GetKeys gets an array object's keys,
// or nil if not an object
GetKeys() []string
// Get gets object's inner value.
// Only works with Object value type
Get(key string) (inner Node)
// Len gets the length of the value
// Only works with Array and String value type
Len() int
// GetN gets array's inner value.
// Only works with Array value type.
// 0 for the first item.
GetN(nth int) Node
// String unmarshal the JSON into string then return
String() (v string)
// Number unmarshal the JSON into float64 then return
Number() (v float64)
// Int unmarshal the JSON into int the return
Int() (v int)
// Bool unmarshal the JSON into bool then return
Bool() (v bool)
// IsNull tells if the JSON value is null or not
IsNull() bool
// ParseError returns the JSON parse error, if any
ParseError() error
}
// NewNode returns an initialized empty Node value
// ready for unmarshaling
func NewNode() Node {
return &rootNode{}
}
// Decode read and decodes a JSON from io.Reader then
// returns a Node of it
func Decode(reader io.Reader) Node {
b, err := ioutil.ReadAll(reader)
return &rootNode{
buf: b,
err: err,
}
}
// rootNode is the default implementation of Node
type rootNode struct {
path string
buf []byte
mapBuf map[string]rootNode
err error
}
// Unmarshal implements Node
func (n *rootNode) Unmarshal(v interface{}) error {
return json.Unmarshal(n.buf, v)
}
// UnmarshalJSON implements Node
func (n *rootNode) UnmarshalJSON(b []byte) error {
n.buf = b
n.mapBuf = nil
return nil
}
// Raw implements Node
func (n *rootNode) Raw() []byte {
return n.buf
}
// Type implements Node
func (n rootNode) Type() Type {
switch {
case n.err != nil:
// for error, return TypeError
return TypeError
case n.buf == nil || len(n.buf) == 0:
// for nil raw, return TypeUndefined
return TypeUndefined
case n.buf[0] == '"':
// simply examine the first character
// to determine the value type
return TypeString
case n.buf[0] == '{':
// simply examine the first character
// to determine the value type
return TypeObject
case n.buf[0] == '[':
// simply examine the first character
// to determine the value type
return TypeArray
case string(n.buf) == "true":
fallthrough
case string(n.buf) == "false":
return TypeBool
case string(n.buf) == "null":
return TypeNull
case isNumJSON(n.buf):
return TypeNumber
}
// return TypeUnknown for all other cases
return TypeError
}
func (n *rootNode) genMapBuf() error {
if n.Type() != TypeObject {
return ErrorNotObject
}
if n.mapBuf != nil {
return nil // previously done, use the previous result
}
// generate the map
n.mapBuf = map[string]rootNode{}
return n.Unmarshal(&n.mapBuf)
}
// GetKeys get object keys of the node.
// If the node is not an object, returns nil
func (n *rootNode) GetKeys() (keys []string) {
if err := n.genMapBuf(); err != nil {
return
}
keys = make([]string, 0, len(n.mapBuf))
for key := range n.mapBuf {
keys = append(keys, key)
}
return
}
func (n *rootNode) keyPath(key string) string {
fmtKey := "." + key
if strings.IndexAny(key, " /-") >= 0 {
fmtKey = fmt.Sprintf("[%#v]", key)
}
return n.path + fmtKey
}
// Get implements Node
func (n *rootNode) Get(key string) (inner Node) {
// the child key path
path := n.keyPath(key)
// if there is previous error, inherit
if err := n.ParseError(); err != nil {
return &rootNode{
path: path,
err: err,
}
}
if err := n.genMapBuf(); err != nil {
if err == ErrorNotObject {
path = n.path // fallback to the parent entity
}
inner = &rootNode{
path: path,
err: Error{
Path: "json" + path,
Err: err,
},
}
} else if val, ok := n.mapBuf[key]; !ok {
inner = &rootNode{
path: path,
err: Error{
Path: "json" + path,
Err: ErrorUndefined,
},
}
} else {
val.path = path
inner = &val
}
return
}
// Len gets the length of the value
// Only works with Array and String value type
func (n *rootNode) Len() int {
switch n.Type() {
case TypeString:
return len(string(n.buf)) - 2 // subtact the 2 " marks
case TypeArray:
vslice := []*rootNode{}
n.Unmarshal(&vslice)
return len(vslice)
}
// default return -1 (for type mismatch)
return -1
}
func (n *rootNode) nthPath(nth int) string {
return fmt.Sprintf("%s[%d]", n.path, nth)
}
// GetN implements Node
func (n *rootNode) GetN(nth int) Node {
// the path to nth node
path := n.nthPath(nth)
// if there is previous error, inherit
if err := n.ParseError(); err != nil {
return &rootNode{
path: path,
err: err,
}
}
if n.Type() != TypeArray {
return &rootNode{
path: n.path,
err: Error{
Path: "json" + n.path,
Err: ErrorNotArray,
},
}
}
vslice := []rootNode{}
n.Unmarshal(&vslice)
if nth < len(vslice) {
vslice[nth].path = path
return &vslice[nth]
}
return &rootNode{
path: path,
err: Error{
Path: "json" + path,
Err: ErrorUndefined,
},
}
}
// String implements Node
func (n *rootNode) String() (v string) {
n.Unmarshal(&v)
return
}
// Number implements Node
func (n *rootNode) Number() (v float64) {
n.Unmarshal(&v)
return
}
// Int implements Node
func (n *rootNode) Int() (v int) {
return int(n.Number())
}
// Bool implements Node
func (n *rootNode) Bool() (v bool) {
n.Unmarshal(&v)
return
}
// IsNull implements Node
func (n *rootNode) IsNull() bool {
return n.Type() == TypeNull
}
// ParseError implements Node
func (n *rootNode) ParseError() error {
return n.err
}