2024-01-23 17:51:52 +01:00
|
|
|
package reflectext
|
|
|
|
|
2024-03-18 11:19:01 +01:00
|
|
|
import (
|
|
|
|
"encoding/json"
|
2024-04-08 16:32:34 +02:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
2024-03-18 11:19:01 +01:00
|
|
|
"reflect"
|
2024-12-10 13:24:06 +01:00
|
|
|
"strings"
|
2024-03-18 11:19:01 +01:00
|
|
|
)
|
2024-01-23 17:51:52 +01:00
|
|
|
|
2024-03-18 11:19:01 +01:00
|
|
|
type ConvertStructToMapOpt struct {
|
|
|
|
KeepJsonMarshalTypes bool
|
2024-04-15 12:55:44 +02:00
|
|
|
MaxDepth *int
|
2024-12-10 13:24:06 +01:00
|
|
|
UseTagsAsKeys *string
|
2024-01-23 17:51:52 +01:00
|
|
|
}
|
|
|
|
|
2024-04-08 16:33:44 +02:00
|
|
|
func ConvertStructToMap(v any, opts ...ConvertStructToMapOpt) map[string]any {
|
2024-03-18 11:19:01 +01:00
|
|
|
opt := ConvertStructToMapOpt{}
|
|
|
|
if len(opts) > 0 {
|
|
|
|
opt = opts[0]
|
|
|
|
}
|
2024-04-08 16:32:34 +02:00
|
|
|
|
2024-04-15 12:55:44 +02:00
|
|
|
res := reflectToMap(reflect.ValueOf(v), 1, opt)
|
2024-04-08 16:32:34 +02:00
|
|
|
|
|
|
|
if v, ok := res.(map[string]any); ok {
|
|
|
|
return v
|
|
|
|
} else if langext.IsNil(res) {
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
panic("not an object")
|
|
|
|
}
|
2024-03-18 11:19:01 +01:00
|
|
|
}
|
|
|
|
|
2024-04-15 12:55:44 +02:00
|
|
|
func reflectToMap(fv reflect.Value, depth int, opt ConvertStructToMapOpt) any {
|
|
|
|
|
|
|
|
if opt.MaxDepth != nil && depth > *opt.MaxDepth {
|
|
|
|
return fv.Interface()
|
|
|
|
}
|
2024-01-23 17:51:52 +01:00
|
|
|
|
|
|
|
if fv.Kind() == reflect.Ptr {
|
|
|
|
|
|
|
|
if fv.IsNil() {
|
|
|
|
return nil
|
|
|
|
} else {
|
2024-04-15 12:55:44 +02:00
|
|
|
return reflectToMap(fv.Elem(), depth, opt)
|
2024-01-23 17:51:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if fv.Kind() == reflect.Func {
|
|
|
|
|
|
|
|
// skip
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if fv.Kind() == reflect.Array {
|
|
|
|
|
|
|
|
arrlen := fv.Len()
|
|
|
|
arr := make([]any, arrlen)
|
|
|
|
for i := 0; i < arrlen; i++ {
|
2024-04-15 12:55:44 +02:00
|
|
|
arr[i] = reflectToMap(fv.Index(i), depth+1, opt)
|
2024-01-23 17:51:52 +01:00
|
|
|
}
|
|
|
|
return arr
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if fv.Kind() == reflect.Slice {
|
|
|
|
|
|
|
|
arrlen := fv.Len()
|
|
|
|
arr := make([]any, arrlen)
|
|
|
|
for i := 0; i < arrlen; i++ {
|
2024-04-15 12:55:44 +02:00
|
|
|
arr[i] = reflectToMap(fv.Index(i), depth+1, opt)
|
2024-01-23 17:51:52 +01:00
|
|
|
}
|
|
|
|
return arr
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if fv.Kind() == reflect.Chan {
|
|
|
|
|
|
|
|
// skip
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if fv.Kind() == reflect.Struct {
|
|
|
|
|
2024-03-18 11:19:01 +01:00
|
|
|
if opt.KeepJsonMarshalTypes && fv.Type().Implements(reflect.TypeFor[json.Marshaler]()) {
|
|
|
|
return fv.Interface()
|
|
|
|
}
|
|
|
|
|
2024-01-23 17:51:52 +01:00
|
|
|
res := make(map[string]any)
|
|
|
|
|
|
|
|
for i := 0; i < fv.NumField(); i++ {
|
|
|
|
if fv.Type().Field(i).IsExported() {
|
2024-12-10 13:24:06 +01:00
|
|
|
|
|
|
|
k := fv.Type().Field(i).Name
|
|
|
|
if opt.UseTagsAsKeys != nil {
|
|
|
|
if tagval, ok := fv.Type().Field(i).Tag.Lookup(*opt.UseTagsAsKeys); ok {
|
|
|
|
if strings.Contains(tagval, ",") {
|
|
|
|
k = strings.TrimSpace(strings.Split(tagval, ",")[0])
|
|
|
|
} else {
|
|
|
|
k = strings.TrimSpace(tagval)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res[k] = reflectToMap(fv.Field(i), depth+1, opt)
|
2024-01-23 17:51:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return fv.Interface()
|
|
|
|
}
|