一、背景和意义
在golang开发中,表现层、业务层、数据层往往定义了各自的struct,经常需要在这些struct间复制数据,复制数据时可能需要忽略字段大小写差异、以及字段类型差异等。本文给出用copier框架实现数据复制的示例。
二、copier框架入门示例
copier框架使用示例如下:
package main
import (
"fmt"
"github.com/jinzhu/copier"
"strconv"
)
type UserReq struct { // 表现层或API层中的用户数据
UserId string
NickName string
Age int32
Salary int
}
type UserEntity struct { // 数据库层中的用户数据
UserID int
Nickname string
Age int32
Salary int
}
func main() {
userReq := UserReq{
UserId: "123",
NickName: "张三",
Age: 18,
Salary: 200000,
}
var userEntity = UserEntity{}
err := copier.Copy(&userEntity, userReq)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("默认模式下的复制结果: %+v \n", userEntity)
}
}
上述代码的输出结果如下:
默认模式下的复制结果: {UserID:0 Nickname:张三 Age:18 Salary:200000}
三、代码解读
在前面的例子中定义了两个存储用户数据的struct,一个是表现层或API层的,另一个是数据库层的。在这个struct在定义时可能由于开发人员的习惯不一样,导致字段上有一些差异。
首先是字段名不一致,UserId和UserID存在大小写差异,Nickname和NickName也一样。
其次就是字段类型存在差异,例如UserReq中的用户ID是字符串,而UserEntity中的用户ID是数字。
从前面的例子看出,copier框架默认情况下会忽略大小写差异,NickName到Nickname之间会正常地复制数据。但字段类型存在差异时没有复制,所以用户ID没有被复制。
四、忽略数字与字符串类型的差异
copier在复制数据时,可以增加Converters,自动地从数字转到字符串或者从字符串转到数字。代码优化为:
package main
import (
"fmt"
"github.com/jinzhu/copier"
"strconv"
)
type UserReq struct { // 表现层或API层中的用户数据
UserId string
NickName string
Age int32
Salary int
}
type UserEntity struct { // 数据库层中的用户数据
UserID int
Nickname string
Age int32
Salary int
}
func main() {
userReq := UserReq{
UserId: "123",
NickName: "张三",
Age: 18,
Salary: 200000,
}
var userEntity = UserEntity{}
err := copier.Copy(&userEntity, userReq)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("默认模式下的复制结果: %+v \n", userEntity)
}
userEntity = UserEntity{}
option := copier.Option{Converters: []copier.TypeConverter{
{
// 源struct是字符串,目标struct是int时,自动转换
SrcType: "",
DstType: 0,
Fn: func(src interface{}) (interface{}, error) {
srcStr, ok := src.(string)
if !ok {
return 0, nil
}
return strconv.Atoi(srcStr)
},
},
{
// 源struct是int,目标struct是字符串时,自动转换
SrcType: 0,
DstType: "",
Fn: func(src interface{}) (interface{}, error) {
srcInt, ok := src.(int)
if !ok {
return 0, nil
}
return strconv.Itoa(srcInt), nil
},
},
}}
err = copier.CopyWithOption(&userEntity, userReq, option)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("忽略字符串与数字差异的复制结果:%+v \n", userEntity)
}
}
程序运行结果为:
默认模式下的复制结果: {UserID:0 Nickname:张三 Age:18 Salary:200000}
忽略字符串与数字差异的复制结果:{UserID:123 Nickname:张三 Age:18 Salary:200000}
虽然用户ID类型不同,但也被复制了。