-
Notifications
You must be signed in to change notification settings - Fork 0
/
decode.go
133 lines (116 loc) · 3.05 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
package structmap
import (
"fmt"
"reflect"
)
// decode a map to a struct
func Unmarshal(src map[string]interface{}, dst interface{}) (err error) {
return decode(src, dst)
}
func decode(src interface{}, dst interface{}) (err error) {
var (
srcValue, dstValue reflect.Value
srcType, dstType reflect.Type
srcKind, dstKind reflect.Kind
)
srcValue = reflect.ValueOf(src)
srcKind = srcValue.Kind()
if srcValue.IsValid() {
srcType = srcValue.Type()
}
dstValue = reflect.ValueOf(dst)
dstKind = dstValue.Kind()
if dstValue.IsValid() {
dstType = dstValue.Type()
}
switch dstKind {
case reflect.Ptr, reflect.Interface:
if dstValue.Elem().Kind() == reflect.Ptr || dstValue.Elem().Kind() == reflect.Interface {
if !dstValue.Elem().IsNil() {
return decode(src, dstValue.Elem().Addr().Interface())
}
}
dstValue = dstValue.Elem()
dstType = dstValue.Type()
dstKind = dstValue.Kind()
}
switch srcKind {
case reflect.Map:
keyKind := srcType.Key().Kind()
if keyKind == reflect.String {
srcMap := src.(map[string]interface{})
switch dstKind {
case reflect.Map:
if dstType.Key().Kind() == reflect.String {
dstMap := dst.(map[string]interface{})
for key, value := range srcMap {
dstMap[key] = value
}
}
case reflect.Struct:
structMap := make(map[string]interface{})
var inlineStruct interface{}
var hasInlineMap bool
var hasInlineStruct bool
inlineMap := make(map[string]interface{})
hasInline := false
for i := 0; i < dstType.NumField(); i++ {
field := dstType.Field(i)
fVal := dstValue.Field(i)
name, opts := scanTag(field)
if name == "-" {
continue
}
isInline := false
_, isInline = opts["inline"]
res := fVal
if isInline {
if fVal.Type() == reflect.TypeOf(inlineMap) {
hasInlineMap = true
if isInline {
if !hasInline {
hasInline = !hasInline
res.Set(reflect.ValueOf(inlineMap))
} else {
fmt.Println("warning, structmap detected multiple inline tags on", dstType.String())
}
}
} else if fVal.Kind() == reflect.Struct {
inlineStruct = res.Addr().Interface()
}
} else {
structMap[name] = res.Addr().Interface()
}
}
for key, value := range srcMap {
if _, ok := structMap[key]; ok {
if err = decode(value, structMap[key]); err != nil {
return
}
} else if hasInlineStruct {
return decode(map[string]interface{}{key: value}, &inlineStruct)
} else if hasInlineMap {
var output interface{}
if err = decode(value, &output); err != nil {
return
}
inlineMap[key] = output
}
}
}
} else {
dstValue.Set(srcValue)
}
case reflect.Slice, reflect.Array:
outputSlice := make([]interface{}, dstValue.Len(), dstValue.Len())
if inputSlice, ok := srcValue.Interface().([]interface{}); ok {
for i, value := range inputSlice {
outputSlice[i] = value
}
}
dstValue.Set(reflect.ValueOf(outputSlice))
default:
dstValue.Set(srcValue)
}
return
}