-
Notifications
You must be signed in to change notification settings - Fork 1
/
reflectutil.go
96 lines (84 loc) · 2.83 KB
/
reflectutil.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
package reflectutil
import (
"errors"
"fmt"
"reflect"
"unsafe"
)
type ToMapKey func(string) string
// TitleUnderscore from https://github.com/go-ini/ini/blob/5e97220809ffaa826f787728501264e9114cb834/struct.go#L46
var TitleUnderscore ToMapKey = func(raw string) string {
newstr := make([]rune, 0, len(raw))
for i, chr := range raw {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
if i > 0 {
newstr = append(newstr, '_')
}
chr -= 'A' - 'a'
}
newstr = append(newstr, chr)
}
return string(newstr)
}
// ToMapWithDefault settings
func ToMapWithDefault(v interface{}) (map[string]interface{}, error) {
return ToMap(v, nil, false)
}
// ToMap converts exported fields of a struct to map[string]interface{} - non exported fields are converted to string
func ToMap(v interface{}, tomapkey ToMapKey, unexported bool) (map[string]interface{}, error) {
if tomapkey == nil {
tomapkey = TitleUnderscore
}
kv := make(map[string]interface{})
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
switch typ.Kind() {
case reflect.Ptr:
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
return nil, errors.New("only structs are supported")
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldName := tomapkey(field.Name)
fieldvalue := val.Field(i)
var fieldValueItf interface{}
if fieldvalue.CanInterface() {
fieldValueItf = fieldvalue.Interface()
} else if unexported {
fieldValueItf = getUnexportedField(fieldvalue)
}
if fieldValueItf != nil {
kv[fieldName] = fieldValueItf
}
}
return kv, nil
}
// we are not particularly interested to preserve the type, so just return the value as string
func getUnexportedField(field reflect.Value) interface{} {
return fmt.Sprint(field)
}
// GetStructField obtains a reference to a field of a pointer to a struct
func GetStructField(structInstance interface{}, fieldname string) reflect.Value {
return reflect.ValueOf(structInstance).Elem().FieldByName(fieldname)
}
// GetUnexportedField unwraps an unexported field with pointer to struct and field name
func GetUnexportedField(structInstance interface{}, fieldname string) interface{} {
field := GetStructField(structInstance, fieldname)
return UnwrapUnexportedField(field)
}
// UnwrapUnexportedField unwraps an unexported field
func UnwrapUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
// SetUnexportedField sets (pointer to) struct's field with the specified value
func SetUnexportedField(structInstance interface{}, fieldname string, value interface{}) {
field := GetStructField(structInstance, fieldname)
setUnexportedField(field, value)
}
func setUnexportedField(field reflect.Value, value interface{}) {
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
Elem().
Set(reflect.ValueOf(value))
}