1. 简介

github仓库地址:https://github.com/jinzhu/copier

文档地址:https://pkg.go.dev/github.com/jinzhu/copier

copier 是 Go 语言的一个实现不同结构体之间成员复制的库。

它可以实现不同结构体的对象到对象的复制、对象到 slice 的复制、slice 到 slice 的复制,支持同名方法和成员、同名成员和方法的复制,还支持 map 到 map 间的复制。

复制结构体成员默认基于成员名称,可以给成员添加标签实现不同的特性。

type Employee struct 
	Name string `copier:"must"` // 如果未复制则 panic
	Age int32 `copier:"must,nopanic"` // 如果未复制则返回 error
	Salary int `copier:"-"` // 忽略该字段
	EmployeeId int64 `copier:"EmployeeNum"` // 指定复制的字段索引,源结构体和目标结构体都要定义
}

copier 的实现基于反射,用于对性能没有太多要求的场景,能简化代码量,提高开发效率。在对性能要求较高的场景,还是建议通过手动列举字段的方式进行复制。

2. 使用

使用 go get 将 copier 包下载到 GOPATH 指定的目录下。

go get github.com/jinzhu/copier

主要的复制方法有两个,不带配置的和带有配置的复制。

// Copy 直接复制
func Copy(toValue interface{}, fromValue interface{}) (err error)

// CopyWithOption 带有配置的复制
func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err error)

配置相关的结构体如下:

// Option 配置
type Option struct {
	IgnoreEmpty   bool // 忽略复制零值的成员
	CaseSensitive bool // 区分大小写
	DeepCopy      bool // 深度复制
	Converters    []TypeConverter // 指定类型转化的逻辑
	FieldNameMapping []FieldNameMapping // 指定对应成员复制的映射表
}

// TypeConverter 类型转化的逻辑
type TypeConverter struct {
	SrcType interface{}
	DstType interface{}
	Fn      func(src interface{}) (dst interface{}, err error)
}

// FieldNameMapping 对应成员复制的映射表
type FieldNameMapping struct {
	SrcType interface{}
	DstType interface{}
	Mapping map[string]string
}

不带配置的复制。

// User 源结构体
type User struct {
	Name         string
	Role         string
	Age          int32
	EmployeeCode int64 `copier:"EmployeeNum"` // 指定复制的索引
	Salary       int
}

func (u *User) DoubleAge() int32 { // 复制时匹配到目标结构体同名字段后执行
	return u.Age * 2
}

// Employee 目标结构体
type Employee struct {
	Name         string
	Age          int32
	DoubleAge    int32
	EmployeeId   int64 `copier:"EmployeeNum"` // 指定复制的索引
	Salary       int   `copier:"-"`           // 忽略复制
	EmployeeRole string
}

func (e *Employee) Role(role string) { // 复制时匹配到源结构体同名字段后执行
	e.EmployeeRole = role
}

func main() {
	var (
		user     = User{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 200000}
		employee = Employee{Salary: 150000}
	)

	// 复制
	copier.Copy(&employee, &user)

	fmt.Printf("%#v \n", employee)
	// main.Employee{Name:"Jinzhu", Age:18, DoubleAge:36, EmployeeId:0, Salary:150000, EmployeeRole:"Admin"}
}

带有配置的复制。

copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true})

map 到 map 的复制。

	map1 := map[int]int{3: 6, 4: 8}
	map2 := map[int32]int8{}
	copier.Copy(&map2, map1)

	fmt.Printf("%#v \n", map2)
	// map[int32]int8{3:6, 4:8}