package reflectext import ( "encoding/json" "gogs.mikescher.com/BlackForestBytes/goext/langext" "reflect" ) type ConvertStructToMapOpt struct { KeepJsonMarshalTypes bool MaxDepth *int } func ConvertStructToMap(v any, opts ...ConvertStructToMapOpt) map[string]any { opt := ConvertStructToMapOpt{} if len(opts) > 0 { opt = opts[0] } res := reflectToMap(reflect.ValueOf(v), 1, opt) if v, ok := res.(map[string]any); ok { return v } else if langext.IsNil(res) { return nil } else { panic("not an object") } } func reflectToMap(fv reflect.Value, depth int, opt ConvertStructToMapOpt) any { if opt.MaxDepth != nil && depth > *opt.MaxDepth { return fv.Interface() } if fv.Kind() == reflect.Ptr { if fv.IsNil() { return nil } else { return reflectToMap(fv.Elem(), depth, opt) } } 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++ { arr[i] = reflectToMap(fv.Index(i), depth+1, opt) } return arr } if fv.Kind() == reflect.Slice { arrlen := fv.Len() arr := make([]any, arrlen) for i := 0; i < arrlen; i++ { arr[i] = reflectToMap(fv.Index(i), depth+1, opt) } return arr } if fv.Kind() == reflect.Chan { // skip return nil } if fv.Kind() == reflect.Struct { if opt.KeepJsonMarshalTypes && fv.Type().Implements(reflect.TypeFor[json.Marshaler]()) { return fv.Interface() } res := make(map[string]any) for i := 0; i < fv.NumField(); i++ { if fv.Type().Field(i).IsExported() { res[fv.Type().Field(i).Name] = reflectToMap(fv.Field(i), depth+1, opt) } } return res } return fv.Interface() }