-
Notifications
You must be signed in to change notification settings - Fork 1
/
unmarshal.go
183 lines (160 loc) · 4.26 KB
/
unmarshal.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
package rejson
import (
"errors"
"fmt"
"reflect"
"github.com/tidwall/gjson"
)
var (
ErrFieldCannotSet = errors.New("field cannot set")
ErrUnexpectJSONValue = errors.New("Unexpect JSON value")
ErrUnknownFieldType = errors.New("Unknown field type")
)
type unmarshal struct {
r gjson.Result
}
func newUnmarshal(jsonString string) *unmarshal {
return &unmarshal{
r: gjson.Parse(jsonString),
}
}
func unmarshalResult(r gjson.Result, v interface{}) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return errors.New("the value must be a non-nil pointer")
}
structValue := rv.Elem()
numField := structValue.NumField()
valueType := structValue.Type()
for i := 0; i < numField; i++ {
fieldType := valueType.Field(i)
tag := parseTag(fieldType.Tag.Get(tagName))
switch tag.Type {
case tagTypePath:
// json path value
val := r.Get(tag.Value)
// set value
if err := setField(structValue.Field(i), val); err != nil {
return err
}
case tagTypeIgnore:
// do nothing
case tagTypeEmpty:
// do nothing
case tagTypeFunc:
callFunc(r, tag.Value, structValue)
default:
return fmt.Errorf("%w: %s", ErrUnknownTag, tag.Type)
}
}
return nil
}
func callFunc(r gjson.Result, funcName string, structValue reflect.Value) {
method := structValue.Addr().MethodByName(funcName)
jsonResultType := reflect.TypeOf((*gjson.Result)(nil))
if method.IsValid() && method.Type().NumIn() == 1 && method.Type().In(0) == jsonResultType {
// TODO handle error
method.Call([]reflect.Value{
reflect.ValueOf(&r),
})
}
// TODO print error log and return error
}
func setFieldStringOrNumber(field reflect.Value, val gjson.Result) error {
fieldType := field.Kind()
switch fieldType {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
field.SetInt(val.Int())
case reflect.Float32, reflect.Float64:
field.SetFloat(val.Float())
case reflect.String:
field.SetString(val.String())
default:
return fmt.Errorf("setFieldStringOrNumber: %w %v", ErrUnknownFieldType, fieldType)
}
return nil
}
func setFieldBool(field reflect.Value, val gjson.Result) error {
if field.Kind() == reflect.Bool {
field.SetBool(val.Bool())
}
return nil
}
func setFieldObject(field reflect.Value, val gjson.Result) error {
var v reflect.Value
if field.Type().Kind() == reflect.Ptr {
v = reflect.New(field.Type().Elem())
} else {
v = reflect.New(field.Type())
}
if err := unmarshalResult(val, v.Interface()); err != nil {
return err
}
if field.Kind() == reflect.Ptr {
// field type is *Entity
field.Set(v)
} else {
// field type is Entity
field.Set(v.Elem())
}
return nil
}
func setFieldArray(field reflect.Value, val gjson.Result) error {
arr := val.Array()
arrLength := len(arr)
var arrVal reflect.Value
if field.Type().Kind() == reflect.Ptr {
arrVal = reflect.MakeSlice(field.Type().Elem(), arrLength, arrLength)
} else {
arrVal = reflect.MakeSlice(field.Type(), arrLength, arrLength)
}
for i := 0; i < arrLength; i++ {
v := arr[i]
if err := setField(arrVal.Index(i), v); err != nil {
return err
}
}
if field.Kind() == reflect.Ptr {
// Users *[]user `rejson:"users"`
fieldVal := reflect.New(field.Type().Elem())
fieldVal.Elem().Set(arrVal)
field.Set(fieldVal)
} else {
field.Set(arrVal)
}
return nil
}
func setField(field reflect.Value, val gjson.Result) error {
if !field.CanSet() {
return fmt.Errorf("setField: %w %v", ErrFieldCannotSet, field.Type())
}
switch val.Type {
case gjson.Number:
return setFieldStringOrNumber(field, val)
case gjson.String:
return setFieldStringOrNumber(field, val)
case gjson.Null:
// not set by default
return nil
case gjson.False:
return setFieldBool(field, val)
case gjson.True:
return setFieldBool(field, val)
case gjson.JSON:
if val.IsObject() {
return setFieldObject(field, val)
} else if val.IsArray() {
return setFieldArray(field, val)
}
}
// should not run to here
return fmt.Errorf("setField: %w %v", ErrUnexpectJSONValue, val)
}
func (u *unmarshal) Unmarshal(v interface{}) error {
return unmarshalResult(u.r, v)
}
// Unmarshal parses the JSON string and stores the result in the value pointed to by v.
func Unmarshal(jsonString string, v interface{}) error {
u := newUnmarshal(jsonString)
return u.Unmarshal(v)
}