package nmapper import ( "errors" "git.noahlan.cn/noahlan/ntool/internal/convert" "git.noahlan.cn/noahlan/ntool/nmap" "git.noahlan.cn/noahlan/ntool/nmapper/wrapper" "git.noahlan.cn/noahlan/ntool/nmath" "git.noahlan.cn/noahlan/ntool/nreflect" "git.noahlan.cn/noahlan/ntool/nstr" "git.noahlan.cn/noahlan/ntool/nstruct" "reflect" "time" ) type MapperObject struct { ZeroValue reflect.Value typeWrappers []wrapper.TypeWrapper timeType reflect.Type // 时间类型 enabledTypeChecking bool // 是否开启类型检查 customTagName string // 自定义Tag名称 } func NewMapper(opts ...Option) *MapperObject { tmp := &MapperObject{ ZeroValue: reflect.Value{}, timeType: reflect.TypeOf(time.Time{}), typeWrappers: make([]wrapper.TypeWrapper, 0), enabledTypeChecking: false, customTagName: "", } for _, opt := range opts { opt(tmp) } return tmp } func (m *MapperObject) SetTypeWrapper(wrappers ...wrapper.TypeWrapper) { m.typeWrappers = wrappers } func (m *MapperObject) RegisterTypeWrapper(wrappers ...wrapper.TypeWrapper) { m.typeWrappers = append(m.typeWrappers, wrappers...) } // MapperObject 将src转换为dst // src为struct类型, dst为map类型 map[string]any 或者 map[string]string func (m *MapperObject) Mapper(dst any, src any) error { srcElem := reflect.ValueOf(src) srcElem = nreflect.Indirect(srcElem) dstElem := reflect.ValueOf(dst) dstElem = nreflect.Indirect(dstElem) if srcElem == m.ZeroValue { return errors.New("src is not legal value") } if dstElem == m.ZeroValue { return errors.New("dst is not legal value") } return m.elemMapper(dstElem, srcElem) } func (m *MapperObject) elemMapper(dstElem, srcElem reflect.Value) error { //if dstElem.Type().Kind() != reflect.Map { // return errors.New(fmt.Sprintf("dst is not legal value, need map but got %s", dstElem.Type().Kind().String())) //} if dstElem.Type().Kind() == reflect.Map { m.elemToMap(dstElem, srcElem) } return nil } func (m *MapperObject) elemToMap(dstElem, srcElem reflect.Value) { for i := 0; i < srcElem.NumField(); i++ { srcFieldInfo := srcElem.Field(i) // tag name, tagMap := m.getFieldNameAndMap(srcElem, i) if name == "" { continue } valElem := srcFieldInfo for _, w := range m.typeWrappers { if w.Supported(srcFieldInfo) { valElem = w.Wrap(srcFieldInfo, i, tagMap) break } } dstElem.SetMapIndex(reflect.ValueOf(name), valElem) } } func (m *MapperObject) ParseMap(dst any, src map[string]any) error { dstElem := reflect.ValueOf(dst) dstElem = nreflect.Indirect(dstElem) if dstElem == m.ZeroValue { return errors.New("dst is not legal value") } for i := 0; i < dstElem.NumField(); i++ { dstFieldInfo := dstElem.Field(i) name, tagMap := m.getFieldNameAndMap(dstElem, i) if name == "" { continue } m.setFieldValue(dstFieldInfo, tagMap, src[name]) } return nil } func (m *MapperObject) ParseStrMap(dst any, src map[string]string) error { dstElem := reflect.ValueOf(dst) dstElem = nreflect.Indirect(dstElem) if dstElem == m.ZeroValue { return errors.New("dst is not legal value") } for i := 0; i < dstElem.NumField(); i++ { dstFieldInfo := dstElem.Field(i) name, tagMap := m.getFieldNameAndMap(dstElem, i) if name == "" { continue } m.setFieldValue(dstFieldInfo, tagMap, src[name]) } return nil } func (m *MapperObject) setFieldValue(fieldElem reflect.Value, tagMap nmap.SMap, value any) { fieldKind := fieldElem.Type().Kind() switch fieldKind { case reflect.Bool: if value == nil { fieldElem.SetBool(false) } else if v, ok := value.(bool); ok { fieldElem.SetBool(v) } else { b, _ := convert.StrToBool(nstr.SafeString(value)) fieldElem.SetBool(b) } case reflect.String: if value == nil { fieldElem.SetString("") } else { fieldElem.SetString(nstr.SafeString(value)) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if value == nil { fieldElem.SetInt(0) } else { val := reflect.ValueOf(value) switch val.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fieldElem.SetInt(val.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: fieldElem.SetInt(int64(val.Uint())) default: v, _ := nmath.ToInt64(value) fieldElem.SetInt(v) } } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if value == nil { fieldElem.SetUint(0) } else { val := reflect.ValueOf(value) switch val.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fieldElem.SetUint(uint64(val.Int())) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: fieldElem.SetUint(val.Uint()) default: v, _ := nmath.ToUint(value) fieldElem.SetUint(v) } } case reflect.Float64, reflect.Float32: if value == nil { fieldElem.SetFloat(0) } else { val := reflect.ValueOf(value) switch val.Kind() { case reflect.Float64: fieldElem.SetFloat(val.Float()) default: fieldElem.SetFloat(nmath.QuietFloat(value)) } } case reflect.Struct: if value == nil { fieldElem.Set(reflect.Zero(fieldElem.Type())) } else { for _, w := range m.typeWrappers { if w.Supported(fieldElem) { w.Set(fieldElem, value, tagMap) break } } if reflect.ValueOf(value).Type() == fieldElem.Type() { fieldElem.Set(reflect.ValueOf(value)) } } default: if !fieldElem.IsValid() { fieldElem.Set(reflect.Zero(fieldElem.Type())) return } // Type check if reflect.ValueOf(value).Type() == fieldElem.Type() { fieldElem.Set(reflect.ValueOf(value)) } } } // getFieldNameAndMap 获取字段名称和tagMap func (m *MapperObject) getFieldNameAndMap(objElem reflect.Value, index int) (string, nmap.SMap) { field := objElem.Type().Field(index) // 1. custom tagVal, ok := field.Tag.Lookup(m.customTagName) if ok && tagVal != "" { sMap, _ := nstruct.ParseTagValueDefault(field.Name, tagVal) name := sMap.Get("name") if name != "" { return name, sMap } else { // tag "-" return "", nil } } // 2. mapper tagVal, ok = field.Tag.Lookup(mapperTagKey) if ok && tagVal != "" { sMap, _ := nstruct.ParseTagValueDefault(field.Name, tagVal) name := sMap.Get("name") if name != "" { return name, sMap } else { // tag "-" return "", nil } } // 3. json tagVal, ok = field.Tag.Lookup(jsonTagKey) if ok && tagVal != "" { sMap, _ := nstruct.ParseTagValueDefault(field.Name, tagVal) name := sMap.Get("name") if name != "" { return name, sMap } else { // tag "-" return "", nil } } return field.Name, nmap.SMap{"name": field.Name} }