码迷,mamicode.com
首页 > 其他好文 > 详细

go反射实现实体映射

时间:2020-05-06 18:04:11      阅读:58      评论:0      收藏:0      [点我收藏+]

标签:for   广州   func   dna   user   string   ret   com   内容   

源码地址:https://github.com/marshhu/ma-tools

项目中经常要用到实体映射,以前做.net是用AutoMapper做的实体映射,感觉挺方便的。

然而最近做的golang项目,还是比较原始的手动赋值,弄起来挺痛苦的,实在受不了,动手写了个简单的实体映射工具方法,代码如下: 

func MapTo(src interface{}, dst interface{}) error {
    srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)
    dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
    //如果src是指针
    if srcType.Kind() == reflect.Ptr {
        srcType, srcValue = srcType.Elem(), srcValue.Elem() // 取具体内容
    }

    if dstValue.Kind() != reflect.Ptr || dstValue.IsNil() {
        return errors.New("dst is not a pointer or is nil")
    }

    dstType, dstValue = dstType.Elem(), dstValue.Elem()
    switch srcType.Kind() {
    case reflect.Struct: //结构体
        item := reflect.New(dstValue.Type())
        setValue(srcValue, item)
        if dstValue.CanSet() {
            dstValue.Set(item.Elem())
        }
    case reflect.Slice: //切片
        if dstType.Kind() != reflect.Slice {
            //fmt.Println(dstType.Kind())
            return errors.New("dst type should be a slice")
        }
        for i := 0; i < srcValue.Len(); i++ {
            //fmt.Println(srcValue.Index(i))
            item := reflect.New(dstValue.Type().Elem())
            setValue(srcValue.Index(i), item)
            if dstValue.CanSet() {
                dstValue.Set(reflect.Append(dstValue, item.Elem()))
            }
        }
    case reflect.Array: //数组
        if dstType.Kind() != reflect.Slice && dstType.Kind() != reflect.Array {
            //fmt.Println(dstType.Kind())
            return errors.New("dst type should be a slice or a array")
        }
        if dstType.Kind() == reflect.Array {
            if dstValue.Len() < srcValue.Len() {
                return errors.New("dst array length should grater then src")
            }
            for i := 0; i < srcValue.Len(); i++ {
                fmt.Println(srcValue.Index(i))
                item := reflect.New(dstValue.Type().Elem())
                setValue(srcValue.Index(i), item)
                if dstValue.Index(i).CanSet() {
                    dstValue.Index(i).Set(item.Elem())
                }
            }
        }
        if dstType.Kind() == reflect.Slice {
            for i := 0; i < srcValue.Len(); i++ {
                //fmt.Println(srcValue.Index(i))
                item := reflect.New(dstValue.Type().Elem())
                setValue(srcValue.Index(i), item)
                if dstValue.CanSet() {
                    dstValue.Set(reflect.Append(dstValue, item.Elem()))
                }
            }
        }
    case reflect.Map: //map
        if dstType.Kind() != reflect.Map { //源数据为切片,要求目标也为map
            return errors.New("dst type should be a map")
        }
        for _, key := range srcValue.MapKeys() {
            //fmt.Println(srcValue.MapIndex(key))
            item := reflect.New(dstValue.Type())
            setValue(srcValue.MapIndex(key), item)
            dstValue.SetMapIndex(key, srcValue.MapIndex(key))
        }
    default:
        panic(fmt.Sprintf("%v cannot mapping", srcType.Kind()))
    }
    return nil
}

func setValue(srcValue reflect.Value, dstValue reflect.Value) error {
    if dstValue.Kind() != reflect.Ptr || dstValue.IsNil() {
        return errors.New("dst is not a pointer or is nil")
    }
    dstType, dstValue := dstValue.Type().Elem(), dstValue.Elem()
    if srcValue.Kind() == reflect.Struct {
        if dstValue.Kind() != reflect.Struct {
            return errors.New("dst type should be a struct pointer")
        }
        for i := 0; i < dstValue.NumField(); i++ {
            fieldInfo := dstType.Field(i)
            fieldName := fieldInfo.Name
            value := srcValue.FieldByName(fieldName)
            if !value.IsValid() || value.Kind() != fieldInfo.Type.Kind() {
                continue
            }
            if value.Kind() == reflect.Struct {
                item := reflect.New(dstValue.Field(i).Type())
                setValue(value, item)
                if dstValue.Field(i).CanSet() {
                    dstValue.Field(i).Set(item.Elem())
                }
            } else {
                if dstValue.Field(i).IsValid() && dstValue.Field(i).CanSet() {
                    dstValue.Field(i).Set(value)
                }
            }
        }
    } else {
        if dstValue.CanSet() {
            dstValue.Set(srcValue)
        }
    }
    return nil
}

测试代码:

func main() {
    type Address struct {
        Country string
        City    string
    }

    type AddressB struct {
        Country string
    }

    type User struct {
        ID      int
        Name    string
        Gender  int
        Tel     string
        Address Address
    }

    type UserDto struct {
        ID      int
        Name    string
        Avatar  string
        Address AddressB
    }

    user := User{1, "小明", 1, "18800188001", Address{"中国", "深圳"}}
    fmt.Printf("user:%v\n", user)
    userDto := &UserDto{}
    err := mapping.MapTo(user, userDto)
    if err != nil {
        fmt.Println(err.Error())
    }
    fmt.Printf("userDto:%v\n", userDto)

    users := []User{
        {1, "小明", 1, "18800188001", Address{"中国", "深圳"}},
        {2, "小红", 0, "18800188002", Address{"中国", "广州"}},
        {3, "小李", 0, "18800188003", Address{"中国", "武汉"}},
        {4, "小张", 1, "18800188004", Address{"中国", "北京"}},
    }
    fmt.Printf("users:%v\n", users)
    var userDtos []UserDto
    err = mapping.MapTo(users, &userDtos)
    if err != nil {
        fmt.Println(err.Error())
    }
    fmt.Printf("userDtos:%v\n", userDtos)
}

运行结果如下:

技术图片

go反射实现实体映射

标签:for   广州   func   dna   user   string   ret   com   内容   

原文地址:https://www.cnblogs.com/marshhu/p/12837834.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!