You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ntool/nmapper/mapper_object.go

266 lines
6.6 KiB
Go

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}
}