diff --git a/maputil/map.go b/maputil/map.go index 008f641..06106b9 100644 --- a/maputil/map.go +++ b/maputil/map.go @@ -307,8 +307,7 @@ func HasKey[K comparable, V any](m map[K]V, key K) bool { // MapToStruct converts map to struct // Play: todo -func MapToStruct(m map[string]interface{}, structObj interface{}) error { - +func MapToStruct(m map[string]any, structObj any) error { for k, v := range m { err := setStructField(structObj, k, v) if err != nil { @@ -341,7 +340,12 @@ func setStructField(structObj any, fieldName string, fieldValue any) error { if fieldVal.Type() != val.Type() { - if m, ok := fieldValue.(map[string]interface{}); ok { + if val.CanConvert(fieldVal.Type()) { + fieldVal.Set(val.Convert(fieldVal.Type())) + return nil + } + + if m, ok := fieldValue.(map[string]any); ok { if fieldVal.Kind() == reflect.Struct { return MapToStruct(m, fieldVal.Addr().Interface()) diff --git a/maputil/typemap.go b/maputil/typemap.go index 1108531..cb20961 100644 --- a/maputil/typemap.go +++ b/maputil/typemap.go @@ -62,18 +62,25 @@ var _ = func() struct{} { */ // Play: https://go.dev/play/p/4K7KBEPgS5M func MapTo(src any, dst any) error { - dstRef := reflect.ValueOf(dst) + if dstRef.Kind() != reflect.Ptr { return fmt.Errorf("dst is not ptr") } + dstElem := dstRef.Type().Elem() + if dstElem.Kind() == reflect.Struct { + srcMap := src.(map[string]interface{}) + return MapToStruct(srcMap, dst) + } + dstRef = reflect.Indirect(dstRef) srcRef := reflect.ValueOf(src) if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface { srcRef = srcRef.Elem() } + if f, ok := mapHandlers[srcRef.Kind()]; ok { return f(srcRef, dstRef) } @@ -121,10 +128,7 @@ func convertSlice(src reflect.Value, dst reflect.Value) error { func convertMap(src reflect.Value, dst reflect.Value) error { if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct { - // if src.Kind() == reflect.Map { - // return convertMap(src, dst) - // } else - if src.Kind() == reflect.Interface { + if src.Kind() == reflect.Interface && dst.IsValid() { return convertMap(src.Elem(), dst) } else { return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String()) @@ -132,7 +136,9 @@ func convertMap(src reflect.Value, dst reflect.Value) error { } dstType := dst.Type() num := dstType.NumField() + exist := map[string]int{} + for i := 0; i < num; i++ { k := dstType.Field(i).Tag.Get("json") if k == "" { @@ -141,7 +147,6 @@ func convertMap(src reflect.Value, dst reflect.Value) error { if strings.Contains(k, ",") { taglist := strings.Split(k, ",") if taglist[0] == "" { - k = dstType.Field(i).Name } else { k = taglist[0] @@ -153,9 +158,11 @@ func convertMap(src reflect.Value, dst reflect.Value) error { } keys := src.MapKeys() + for _, key := range keys { if index, ok := exist[key.String()]; ok { v := dst.Field(index) + if v.Kind() == reflect.Struct { err := convertMap(src.MapIndex(key), v) if err != nil { diff --git a/maputil/typemap_test.go b/maputil/typemap_test.go index 4c5d47a..cba9218 100644 --- a/maputil/typemap_test.go +++ b/maputil/typemap_test.go @@ -8,10 +8,10 @@ import ( type ( Person struct { - Name string `json:"name"` - Age int `json:"age"` - Phone string `json:"phone"` - Address Address `json:"address"` + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Address *Address `json:"address"` } Address struct { @@ -38,6 +38,7 @@ func TestStructType(t *testing.T) { var p Person err := MapTo(src, &p) assert.IsNil(err) + assert.Equal(src["name"], p.Name) assert.Equal(src["age"], p.Age) assert.Equal(src["phone"], p.Phone)