源码地址: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().Elem())
setValue(srcValue.MapIndex(key), item)
if dstValue.CanSet() {
dstValue.SetMapIndex(key, item.Elem())
}
}
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
}
测试代码:
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
}
func TestMapToStruct(t *testing.T) {
user := User{1, "小明", 1, "18800188001", Address{"中国", "深圳"}}
userDto := &UserDto{}
err := MapTo(user, userDto)
if err != nil {
t.FailNow()
}
if userDto.ID != user.ID && userDto.Name != user.Name && userDto.Address.Country != user.Address.Country {
t.FailNow()
}
}
func TestMapToSlice(t *testing.T) {
users := []User{
{1, "小明", 1, "18800188001", Address{"中国", "深圳"}},
{2, "小红", 0, "18800188002", Address{"中国", "广州"}},
{3, "小李", 0, "18800188003", Address{"中国", "武汉"}},
{4, "小张", 1, "18800188004", Address{"中国", "北京"}},
}
var userDtos []UserDto
err := MapTo(users, &userDtos)
if err != nil {
t.FailNow()
}
if len(userDtos) != len(users) {
t.FailNow()
}
for i := 0; i < len(users); i++ {
userDto := userDtos[i]
user := users[i]
if userDto.ID != user.ID && userDto.Name != user.Name && userDto.Address.Country != user.Address.Country {
t.FailNow()
}
}
}
func TestMapToArray(t *testing.T) {
users := [4]User{
{1, "小明", 1, "18800188001", Address{"中国", "深圳"}},
{2, "小红", 0, "18800188002", Address{"中国", "广州"}},
{3, "小李", 0, "18800188003", Address{"中国", "武汉"}},
{4, "小张", 1, "18800188004", Address{"中国", "北京"}},
}
var userDtos [4]UserDto
err := MapTo(users, &userDtos)
if err != nil {
t.FailNow()
}
if len(userDtos) != len(users) {
t.FailNow()
}
for i := 0; i < len(users); i++ {
userDto := userDtos[i]
user := users[i]
if userDto.ID != user.ID && userDto.Name != user.Name && userDto.Address.Country != user.Address.Country {
t.FailNow()
}
}
}
func TestMapToMap(t *testing.T) {
users := make(map[string]User)
users["小明"] = User{1, "小明", 1, "18800188001", Address{"中国", "深圳"}}
users["小红"] = User{2, "小红", 0, "18800188002", Address{"中国", "广州"}}
users["小李"] = User{3, "小李", 0, "18800188003", Address{"中国", "武汉"}}
users["小张"] = User{4, "小张", 1, "18800188004", Address{"中国", "北京"}}
userDtos := make(map[string]UserDto)
err := MapTo(users, &userDtos)
if err != nil {
t.FailNow()
}
if len(userDtos) != len(users) {
t.FailNow()
}
for key := range users {
userDto := userDtos[key]
user := users[key]
if userDto.ID != user.ID && userDto.Name != user.Name && userDto.Address.Country != user.Address.Country {
t.FailNow()
}
}
}
测试结果如下:
原文:https://www.cnblogs.com/marshhu/p/12837834.html