-
Notifications
You must be signed in to change notification settings - Fork 0
/
decode.go
201 lines (170 loc) · 4.95 KB
/
decode.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
package astjson
import (
"errors"
"reflect"
)
const (
jsonTAG = "json"
)
type Decoder struct{}
func NewDecoder() *Decoder {
d := &Decoder{}
return d
}
// Unmarshal decodes the AST to a structure.
// This API is an EXPERIENTIAL one and might be removed in the future.
func (d *Decoder) Unmarshal(val *Value, dest interface{}) error {
if !isPointer(dest) {
return errors.New("dest must be a pointer")
}
return d.unmarshal(val, dest)
}
// todo: think about using reflect.Value instead of interface{} as the type of dest
func (d *Decoder) unmarshal(val *Value, dest interface{}) error {
if val == nil {
return errors.New("value is a nil pointer")
}
// todo: currently I only consider the valid conversion, need ensure behaviors if fail
switch val.NodeType {
case Number:
return setNumber(val, dest)
case String:
return setString(val, dest)
case Null:
return setNull(dest)
case Bool:
return setBool(val, dest)
case Array:
return setArray(val, dest)
case Object:
return setObject(val, dest)
}
return errors.New("invalid value")
}
func setObject(val *Value, dest interface{}) error {
obj := val.AstValue.(*ObjectAst)
// *T --> T
rval := reflect.ValueOf(dest).Elem()
rtyp := reflect.TypeOf(dest).Elem()
var err error
for index := 0; index < rtyp.NumField(); index++ {
fieldTyp := rtyp.Field(index)
tag := fieldTyp.Tag.Get(jsonTAG)
if tag == "" && !fieldTyp.Anonymous {
continue
}
// handle embedded fields
if fieldTyp.Anonymous {
fieldVal := reflect.New(fieldTyp.Type)
err = NewDecoder().unmarshal(val, fieldVal.Interface())
if err != nil {
return err
}
rval.Field(index).Set(fieldVal.Elem())
continue
}
astVal := obj.KvMap[tag]
// construct a pointer type of field type to store the data
// we cannot pass the field directly because it's not a reference but a value, however,
// we want to change the value itself. So pass it in side unmarshal as an interface doesn't work.
fieldVal := reflect.New(rtyp.Field(index).Type)
err = NewDecoder().unmarshal(&astVal, fieldVal.Interface())
if err != nil {
return err
}
rval.Field(index).Set(fieldVal.Elem())
}
return nil
}
// setArray sets the json array into golang a slice or an array.
func setArray(val *Value, dest interface{}) error {
ars := val.AstValue.(*ArrayAst).Values
kind := reflect.TypeOf(dest).Elem().Kind()
if kind != reflect.Array && kind != reflect.Slice {
// todo: ignore error or report it?
return nil
}
boundary := reflect.ValueOf(dest).Elem().Len()
for i, value := range ars {
// all available fields in an array are filled, we needn't to continue
if i >= boundary && kind == reflect.Array {
return nil
}
// double elem get from a pointer of array to the array element type
// *[]T --Elem()--> []T --Elem()--> T
elemType := reflect.TypeOf(dest).Elem().Elem()
newVal := reflect.New(elemType)
err := NewDecoder().unmarshal(&value, newVal.Interface())
if err != nil {
return err
}
elem := newVal.Elem()
// this logic only applies to slice because array has a fixed length.
if i >= boundary {
// append the element into the array,
// instead of the pointer to the array
na := reflect.Append(reflect.ValueOf(dest).Elem(), elem)
reflect.ValueOf(dest).Elem().Set(na)
continue
}
reflect.ValueOf(dest).Elem().Index(i).Set(elem)
}
return nil
}
func setNull(dest interface{}) error {
// pointer owns type, we cannot assign it a nil directly
v := reflect.ValueOf(dest).Elem()
v.Set(reflect.Zero(v.Type()))
return nil
}
func setBool(val *Value, dest interface{}) error {
reflect.ValueOf(dest).Elem().SetBool(bool(val.AstValue.(BoolAst)))
return nil
}
func setNumber(val *Value, dest interface{}) error {
kind := reflect.ValueOf(dest).Elem().Kind()
numberAst := val.AstValue.(NumberAst)
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
reflect.ValueOf(dest).Elem().SetInt(numberAst.GetInt64())
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
reflect.ValueOf(dest).Elem().SetUint(numberAst.GetUint64())
return nil
case reflect.Float32,
reflect.Float64:
reflect.ValueOf(dest).Elem().SetFloat(numberAst.GetFloat64())
return nil
}
panic("fail to set number")
}
// setString set the string value into dest
// todo: support []byte and []int8
// todo: think about whether support the implicitly cast from byte to the other types
func setString(val *Value, dest interface{}) error {
v := reflect.ValueOf(dest).Elem()
kind := v.Kind()
strAst := val.AstValue.(StringAst)
switch kind {
case reflect.String:
v.SetString(string(strAst))
return nil
case reflect.Slice:
v.SetBytes([]byte(strAst))
return nil
case reflect.Array:
l := v.Len()
bs := []byte(strAst)
for i := 0; i < l; i++ {
v.Index(i).Set(reflect.ValueOf(bs[i]))
}
return nil
}
panic("fail to set string")
}
func isPointer(dest interface{}) bool {
if reflect.ValueOf(dest).Kind() != reflect.Pointer {
return false
}
return true
}