package convert import ( "encoding/json" "reflect" "strings" "apigo.cc/go/cast" "gopkg.in/yaml.v3" ) // To 将 from 中的数据深度映射到 to 中。 // 核心哲学:意图优先。根据 to 的类型推断用户的意图,并尽力转化数据。 func To(from, to any) { r := convert(from, to) if r != nil { toValue := reflect.ValueOf(to) var prevValue reflect.Value for toValue.Kind() == reflect.Ptr { prevValue = toValue toValue = toValue.Elem() } if prevValue.IsValid() { prevValue.Elem().Set(*r) } } } // Convert 是 To 的别名,保持向前兼容。 func Convert(from, to any) { To(from, to) } func convert(from, to any) *reflect.Value { var fromValue reflect.Value var toValue reflect.Value if v, ok := from.(reflect.Value); ok { fromValue = v } else { fromValue = reflect.ValueOf(from) } if v, ok := to.(reflect.Value); ok { toValue = v } else { toValue = reflect.ValueOf(to) } // 1. 初始化目标容器 fixNilValue(toValue) // 2. 获取底层业务数据 rawFrom := cast.FinalValue(fromValue) destValue := cast.RealValue(toValue) if !destValue.IsValid() { return nil } // 3. 处理 Unmarshaler 接口 if destValue.CanAddr() { addr := destValue.Addr().Interface() if um, ok := addr.(json.Unmarshaler); ok { _ = um.UnmarshalJSON(cast.JsonBytes(rawFrom.Interface())) return nil } if um, ok := addr.(yaml.Unmarshaler); ok { _ = um.UnmarshalYAML(&yaml.Node{Value: cast.String(rawFrom.Interface())}) return nil } } // 4. 核心转换逻辑 fromType := finalType(rawFrom) destType := destValue.Type() // 兼容 interface{} 目标 if destType.Kind() == reflect.Interface { if destValue.CanSet() { destValue.Set(reflect.ValueOf(rawFrom.Interface())) return nil } } // 极致去摩擦:如果目标是单值,但输入是切片,自动取第一个元素进行后续处理 effectiveFrom := rawFrom if fromType.Kind() == reflect.Slice && rawFrom.Len() > 0 && destType.Kind() != reflect.Slice && destType.Kind() != reflect.Array { effectiveFrom = cast.FinalValue(rawFrom.Index(0)) } var newValue *reflect.Value switch destType.Kind() { case reflect.Bool: setOrNew(destValue, reflect.ValueOf(cast.Bool(effectiveFrom.Interface())), &newValue) case reflect.String: setOrNew(destValue, reflect.ValueOf(cast.String(effectiveFrom.Interface())), &newValue) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: setOrNew(destValue, reflect.ValueOf(cast.Int64(effectiveFrom.Interface())).Convert(destType), &newValue) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: setOrNew(destValue, reflect.ValueOf(cast.Uint64(effectiveFrom.Interface())).Convert(destType), &newValue) case reflect.Float32, reflect.Float64: setOrNew(destValue, reflect.ValueOf(cast.Float64(effectiveFrom.Interface())).Convert(destType), &newValue) case reflect.Slice: if destType.Elem().Kind() == reflect.Uint8 { setOrNew(destValue, reflect.ValueOf(cast.JsonBytes(rawFrom.Interface())), &newValue) } else { workFrom := rawFrom if fromType.Kind() == reflect.String { str := rawFrom.String() if !strings.HasPrefix(str, "[") && strings.Contains(str, ",") { workFrom = reflect.ValueOf(cast.SplitTrim(str, ",")) } else if !strings.HasPrefix(str, "[") { tmp := reflect.MakeSlice(reflect.SliceOf(fromType), 1, 1) tmp.Index(0).Set(rawFrom) workFrom = tmp } else { var arr []any cast.UnJson(str, &arr) workFrom = reflect.ValueOf(arr) } } else if fromType.Kind() != reflect.Slice { tmp := reflect.MakeSlice(reflect.SliceOf(fromType), 1, 1) tmp.Index(0).Set(rawFrom) workFrom = tmp } return convertSliceToSlice(workFrom, destValue) } case reflect.Struct: switch effectiveFrom.Kind() { case reflect.Map: convertMapToStruct(effectiveFrom, destValue) case reflect.Struct: convertStructToStruct(effectiveFrom, destValue) case reflect.String: var m map[string]any cast.UnJson(effectiveFrom.String(), &m) convertMapToStruct(reflect.ValueOf(m), destValue) } case reflect.Map: if destValue.IsNil() { destValue = reflect.MakeMap(destType) newValue = &destValue } switch rawFrom.Kind() { case reflect.Map: convertMapToMap(rawFrom, destValue) case reflect.Struct: convertStructToMap(rawFrom, destValue) case reflect.String: var m map[string]any cast.UnJson(rawFrom.String(), &m) convertMapToMap(reflect.ValueOf(m), destValue) } case reflect.Func: if rawFrom.Kind() == reflect.Func { destValue.Set(reflect.MakeFunc(destType, func(goArgs []reflect.Value) []reflect.Value { ins := make([]reflect.Value, 0) for i := 0; i < destType.NumIn(); i++ { if i < rawFrom.Type().NumIn() { argP := reflect.New(rawFrom.Type().In(i)) convert(goArgs[i].Interface(), argP) ins = append(ins, argP.Elem()) } } out := rawFrom.Call(ins) outs := make([]reflect.Value, 0) for i := 0; i < destType.NumOut(); i++ { outP := reflect.New(destType.Out(i)) if i < len(out) { convert(out[i].Interface(), outP) } outs = append(outs, outP.Elem()) } return outs })) } } return newValue } func setOrNew(dest, val reflect.Value, newValue **reflect.Value) { if dest.CanSet() { dest.Set(val) } else { *newValue = &val } } func fixNilValue(v reflect.Value) { t := v.Type() for t.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } v = v.Elem() t = t.Elem() } if t.Kind() == reflect.Slice && v.IsNil() { v.Set(reflect.MakeSlice(v.Type(), 0, 0)) } if t.Kind() == reflect.Map && v.IsNil() { v.Set(reflect.MakeMap(v.Type())) } } func finalType(v reflect.Value) reflect.Type { if !v.IsValid() { return reflect.TypeOf(nil) } t := v.Type() for t.Kind() == reflect.Ptr { t = t.Elem() } return t } func normalizeKey(s string) string { return strings.Map(func(r rune) rune { if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') { return r } if r >= 'A' && r <= 'Z' { return r + 32 } return -1 }, s) } func convertMapToStruct(from, to reflect.Value) { keys := from.MapKeys() keyMap := make(map[string]*reflect.Value) for i := range keys { keyMap[normalizeKey(cast.String(keys[i].Interface()))] = &keys[i] } toType := to.Type() for i := 0; i < toType.NumField(); i++ { f := toType.Field(i) if f.Anonymous { convertMapToStruct(from, to.Field(i)) continue } if f.Name[0] < 'A' || f.Name[0] > 'Z' { continue } k := keyMap[normalizeKey(f.Name)] if k != nil { val := from.MapIndex(*k) if val.IsValid() { if to.CanAddr() { if m, ok := to.Addr().Type().MethodByName("Parse" + f.Name); ok { argP := reflect.New(m.Type.In(1)) convert(val, argP) out := m.Func.Call([]reflect.Value{to.Addr(), argP.Elem()}) to.Field(i).Set(out[0]) continue } } r := convert(val, to.Field(i)) if r != nil { to.Field(i).Set(*r) } } } } } func convertStructToStruct(from, to reflect.Value) { fromType := from.Type() keyMap := make(map[string]int) for i := 0; i < fromType.NumField(); i++ { f := fromType.Field(i) if f.Name[0] >= 'A' && f.Name[0] <= 'Z' { keyMap[normalizeKey(f.Name)] = i + 1 } } toType := to.Type() for i := 0; i < toType.NumField(); i++ { f := toType.Field(i) if f.Anonymous { convertStructToStruct(from, to.Field(i)) continue } if f.Name[0] < 'A' || f.Name[0] > 'Z' { continue } k := keyMap[normalizeKey(f.Name)] if k != 0 { r := convert(from.Field(k-1), to.Field(i)) if r != nil { to.Field(i).Set(*r) } } } } func convertMapToMap(from, to reflect.Value) { toType := to.Type() for _, k := range from.MapKeys() { keyItem := reflect.New(toType.Key()).Elem() convert(k, keyItem) valueItem := reflect.New(toType.Elem()).Elem() r := convert(from.MapIndex(k), valueItem) if r != nil { to.SetMapIndex(keyItem, *r) } else { to.SetMapIndex(keyItem, valueItem) } } } func convertStructToMap(from, to reflect.Value) { toType := to.Type() fromType := from.Type() for i := 0; i < from.NumField(); i++ { f := fromType.Field(i) if f.Name[0] < 'A' || f.Name[0] > 'Z' { continue } keyItem := reflect.New(toType.Key()).Elem() convert(cast.GetLowerName(f.Name), keyItem) valueItem := reflect.New(toType.Elem()).Elem() r := convert(from.Field(i), valueItem) if r != nil { to.SetMapIndex(keyItem, *r) } else { to.SetMapIndex(keyItem, valueItem) } } } func convertSliceToSlice(from, to reflect.Value) *reflect.Value { toType := to.Type() for i := 0; i < from.Len(); i++ { valueItem := reflect.New(toType.Elem()).Elem() r := convert(from.Index(i), valueItem) if r != nil { to = reflect.Append(to, *r) } else { to = reflect.Append(to, valueItem) } } return &to } type StructInfo struct { Fields []reflect.StructField Values map[string]reflect.Value Methods []reflect.Method MethodValues map[string]reflect.Value } func FlatStruct(data any) *StructInfo { return flat(data, true) } func FlatStructWithUnexported(data any) *StructInfo { return flat(data, false) } func flat(data any, onlyExported bool) *StructInfo { out := &StructInfo{ Fields: []reflect.StructField{}, Values: make(map[string]reflect.Value), Methods: []reflect.Method{}, MethodValues: make(map[string]reflect.Value), } var v reflect.Value if rv, ok := data.(reflect.Value); ok { v = rv } else { v = reflect.ValueOf(data) } makeStructInfo(v, out, onlyExported) return out } func makeStructInfo(v reflect.Value, out *StructInfo, onlyExported bool) { for v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Ptr { v = v.Elem() } fv := v if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { fv = v.Elem() } if fv.Kind() == reflect.Struct { t := v.Type() if v.Kind() == reflect.Ptr { for i := 0; i < v.NumMethod(); i++ { m := t.Method(i) if onlyExported && !m.IsExported() { continue } out.Methods = append(out.Methods, m) out.MethodValues[m.Name] = v.Method(i) } } ft := fv.Type() for i := 0; i < ft.NumField(); i++ { f := ft.Field(i) if onlyExported && !f.IsExported() { continue } if f.Anonymous { makeStructInfo(fv.Field(i), out, onlyExported) } else { out.Fields = append(out.Fields, f) out.Values[f.Name] = fv.Field(i) } } } }