-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnested.go
143 lines (123 loc) · 3.63 KB
/
nested.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
package _struct
import (
"reflect"
"strings"
"github.com/kyaxcorp/go-helper/_int"
"github.com/kyaxcorp/go-helper/errors2/define"
)
func GetNestedFieldReflectValue(val reflect.Value, path string) (reflect.Value, error) {
if path == "" {
//panic("path is empty")
return reflect.Value{}, define.Err(0, "path is empty")
}
keys := strings.Split(path, ".")
nrOfKeys := len(keys)
//log.Println("nr of keys", nrOfKeys)
//log.Println("keys", keys)
if nrOfKeys == 0 {
//panic("nr of keys is 0")
return reflect.Value{}, define.Err(0, "nr of keys is 0")
}
i := reflect.Indirect(val)
if i == (reflect.Value{}) {
//log.Println("IT'S ZERO VALUE")
return reflect.Value{}, define.Err(0, "i is zero value")
}
firstKey := keys[0]
nrOfRemainingKeys := nrOfKeys - 1
//log.Println("nr of remaining keys", nrOfRemainingKeys)
//log.Println("first key", firstKey)
// Check if key is a number, string
// check if string is key for map... maybe we should write this in other way!
if _int.IsNumber(firstKey) {
// TODO:
return reflect.Value{}, nil
} else {
objVal := i.FieldByName(firstKey)
if objVal == (reflect.Value{}) {
panic("field " + firstKey + " doesn't exist")
}
if nrOfRemainingKeys <= 0 {
// it's the last one!
return objVal, nil
} else {
var remainKeys = make([]string, nrOfRemainingKeys)
copy(remainKeys[0:nrOfRemainingKeys], keys[1:nrOfRemainingKeys+1])
newPath := strings.Join(remainKeys, ".")
//log.Println("copy these:", keys[1:nrOfRemainingKeys])
//log.Println("copy these:", keys[1:nrOfRemainingKeys+1])
//log.Println("remain keys", remainKeys)
return GetNestedFieldReflectValue(objVal, newPath)
}
}
}
// nested retrieves recursively all types for the given value and returns the
// nested value.
func (h *Helper) nested(val reflect.Value) interface{} {
var finalVal interface{}
v := reflect.ValueOf(val.Interface())
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
switch v.Kind() {
case reflect.Struct:
n := New(val.Interface())
n.TagName = h.TagName
m := n.Map()
// do not add the converted value if there are no exported fields, ie:
// time.Time
if len(m) == 0 {
finalVal = val.Interface()
} else {
finalVal = m
}
case reflect.Map:
// get the element type of the map
mapElem := val.Type()
switch val.Type().Kind() {
case reflect.Ptr, reflect.Array, reflect.Map,
reflect.Slice, reflect.Chan:
mapElem = val.Type().Elem()
if mapElem.Kind() == reflect.Ptr {
mapElem = mapElem.Elem()
}
}
// only iterate over struct types, ie: map[string]StructType,
// map[string][]StructType,
if mapElem.Kind() == reflect.Struct ||
(mapElem.Kind() == reflect.Slice &&
mapElem.Elem().Kind() == reflect.Struct) {
m := make(map[string]interface{}, val.Len())
for _, k := range val.MapKeys() {
m[k.String()] = h.nested(val.MapIndex(k))
}
finalVal = m
break
}
// TODO(arslan): should this be optional?
finalVal = val.Interface()
case reflect.Slice, reflect.Array:
if val.Type().Kind() == reflect.Interface {
finalVal = val.Interface()
break
}
// TODO(arslan): should this be optional?
// do not iterate of non struct types, just pass the value. Ie: []int,
// []string, co... We only iterate further if it's a struct.
// i.e []foo or []*foo
if val.Type().Elem().Kind() != reflect.Struct &&
!(val.Type().Elem().Kind() == reflect.Ptr &&
val.Type().Elem().Elem().Kind() == reflect.Struct) {
finalVal = val.Interface()
break
}
slices := make([]interface{}, val.Len())
for x := 0; x < val.Len(); x++ {
slices[x] = h.nested(val.Index(x))
}
finalVal = slices
default:
finalVal = val.Interface()
}
return finalVal
}