关于复制或深度克隆,Copier是一个很方便的工具。
当处理复杂的数据结构或嵌套结构时,手动复制数据可能会是一个繁琐且容易出错的任务。
Copier 在这种情况下非常有用。
Copier提供了一系列功能。
GitHub地址:https://github.com/jinzhu/copier
官方文档:https://pkg.go.dev/github.com/jinzhu/copier
go get github.com/jinzhu/copier
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Role string
Age int32
EmployeCode int64 `copier:"EmployeNum"` // 指定字段名称
Salary int // 在目标结构中显式忽略
}
func (user *User) DoubleAge() int32 {
return 2 * user.Age
}
type Employee struct {
// 必须复制,字段未复制则panic
Name string `copier:"must"`
// 必须复制,字段未复制则返回一个error
Age int32 `copier:"must,nopanic"`
// 显式忽略复制此字段
Salary int `copier:"-"`
DoubleAge int32
EmployeId int64 `copier:"EmployeNum"` // 指定字段名称
SuperRole string
}
func (employee *Employee) Role(role string) {
employee.SuperRole = "Super " + role
}
func main() {
var (
user = User{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 200000}
users = []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 100000}, {Name: "jinzhu 2", Age: 30, Role: "Dev", Salary: 60000}}
employee = Employee{Salary: 150000}
employees = []Employee{}
)
copier.Copy(&employee, &user)
/*
Employee{
Name: "Jinzhu", // Copy from field
Age: 18, // Copy from field
Salary:150000, // Copying explicitly ignored
DoubleAge: 36, // Copy from method
EmployeeId: 0, // Ignored
SuperRole: "Super Admin", // Copy to method
}
*/
// main.Employee{Name:"Jinzhu", Age:18, Salary:150000, DoubleAge:36, EmployeId:0, SuperRole:"Super Admin"}
fmt.Printf("%#v \n", employee)
// 复制struct到slice
copier.Copy(&employees, &user)
// []main.Employee{main.Employee{Name:"Jinzhu", Age:18, Salary:0, DoubleAge:36, EmployeId:0, SuperRole:"Super Admin"}}
fmt.Printf("%#v \n", employees)
// 复制slice到slice
employees = []Employee{}
copier.Copy(&employees, &users)
// []
// main.Employee{main.Employee{Name:"Jinzhu", Age:18, Salary:0, DoubleAge:36, EmployeId:0, SuperRole:"Super Admin"},
// main.Employee{Name:"jinzhu 2", Age:30, Salary:0, DoubleAge:60, EmployeId:0, SuperRole:"Super Dev"}}
fmt.Printf("%#v \n", employees)
// 复制map到map
map1 := map[int]int{3: 6, 4: 8}
map2 := map[int32]int8{}
copier.Copy(&map2, map1)
// map[int32]int8{3:6, 4:8}
fmt.Printf("%#v \n", map2)
}
目标对象中的一些字段,源对象中没有,但是源对象有同名的方法。这时 Copy 会调用这个方法,将返回值赋值给
目标对象中的字段。
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
}
func (u *User) DoubleAge() int {
return 2 * u.Age
}
type Employee struct {
Name string
DoubleAge int
Role string
}
func main() {
user := User{Name: "dj", Age: 18}
employee := Employee{}
copier.Copy(&employee, &user)
// main.Employee{Name:"dj", DoubleAge:36, Role:""}
fmt.Printf("%#v\n", employee)
}
我们给User添加一个DoubleAge方法,Employee结构有字段DoubleAge,User中没有,但是User有一个同名的
方法,这时Copy调用user的DoubleAge方法为employee的DoubleAge赋值,得到 36。
有时候源对象中的某个字段没有出现在目标对象中,但是目标对象有一个同名的方法,方法接受一个同类型的参
数,这时 Copy 会以源对象的这个字段作为参数调用目标对象的该方法。
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
Role string
}
type Employee struct {
Name string
Age int
SuperRole string
}
func (e *Employee) Role(role string) {
e.SuperRole = "Super" + role
}
func main() {
user := User{Name: "dj", Age: 18, Role: "Admin"}
employee := Employee{}
copier.Copy(&employee, &user)
// main.Employee{Name:"dj", Age:18, SuperRole:"SuperAdmin"}
fmt.Printf("%#v\n", employee)
}
我们给Employee添加了一个Role方法,User的字段Role没有出现在Employee中,但是Employee有一个同名方
法。Copy函数内部会以user对象的Role字段为参数调用employee的Role方法。最终,我们的employee对象的
SuperRole值变为SuperAdmin。实际上,这个方法中可以执行任何操作,不一定是赋值。
使用一个切片来为另一个切片赋值。如果类型相同,那好办,直接append就行。如果类型不同,copier就派上
大用场了。
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
}
type Employee struct {
Name string
Age int
Role string
}
func main() {
users := []User{
{Name: "dj", Age: 18},
{Name: "dj2", Age: 18},
}
employees := []Employee{}
copier.Copy(&employees, &users)
// []main.Employee{main.Employee{Name:"dj", Age:18, Role:""}, main.Employee{Name:"dj2", Age:18, Role:""}}
fmt.Printf("%#v\n", employees)
}
这个实际上就是将源切片中每个元素分别赋值到目标切片中。
这个不难,实际上就是根据源对象生成一个和目标切片类型相符合的对象,然后 append 到目标切片中。
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
}
type Employee struct {
Name string
Age int
Role string
}
func main() {
user := User{Name: "dj", Age: 18}
employees := []Employee{}
copier.Copy(&employees, &user)
// []main.Employee{main.Employee{Name:"dj", Age:18, Role:""}}
fmt.Printf("%#v\n", employees)
}
上面代码中,Copy先通过user生成一个Employee对象,然后append到切片employees中。
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
}
type Employee struct {
Name string
Age int
Role string
}
func main() {
user := User{Name: "dj", Age: 18}
employee := Employee{}
copier.Copy(&employee, &user)
// main.Employee{Name:"dj", Age:18, Role:""}
fmt.Printf("%#v\n", employee)
}
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
Email string
Password string
}
type SimpleUser struct {
Name string
Age int
}
func main() {
user := User{
Name: "Aiden",
Age: 30,
Email: "[email protected]",
Password: "thisisasecret",
}
s := SimpleUser{}
copier.Copy(&s, &user)
// main.SimpleUser{Name:"Aiden", Age:30}
fmt.Printf("%#v\n", s)
}
最后将所有的特性汇总在一个例子中,其实就是 Copier 的 GitHub 仓库首页的例子。
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
Role string
}
func (u *User) DoubleAge() int {
return u.Age * 2
}
type Employee struct {
Name string
Age int
SuperRole string
}
func (e *Employee) Role(role string) {
e.SuperRole = "Super" + role
}
func main() {
var (
user = User{Name: "dj", Age: 18}
users = []User{
{Name: "dj", Age: 18, Role: "Admin"},
{Name: "dj2", Age: 18, Role: "Dev"},
}
employee = Employee{}
employees = []Employee{}
)
copier.Copy(&employee, &user)
// main.Employee{Name:"dj", Age:18, SuperRole:"Super"}
fmt.Printf("%#v\n", employee)
copier.Copy(&employees, &user)
// []main.Employee{main.Employee{Name:"dj", Age:18, SuperRole:"Super"}}
fmt.Printf("%#v\n", employees)
employees = []Employee{}
copier.Copy(&employees, &users)
// []main.Employee{main.Employee{Name:"dj", Age:18, SuperRole:"SuperAdmin"}, main.Employee{Name:"dj2", Age:18, SuperRole:"SuperDev"}}
fmt.Printf("%#v\n", employees)
// 2
fmt.Printf("%d",len(employees))
}
copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true})
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Age int
Email string
Password string
}
type SimpleUser struct {
Name string
Age int
}
func main() {
user1 := User{
Name: "Aiden",
Age: 30,
Email: "[email protected]",
Password: "thisisasecret",
}
s1 := SimpleUser{}
copier.CopyWithOption(&s1, &user1, copier.Option{IgnoreEmpty: true, DeepCopy: true})
s1.Name = "Tom"
// main.SimpleUser{Name:"Tom", Age:30}
fmt.Printf("%#v\n", s1)
// main.User{Name:"Aiden", Age:30, Email:"[email protected]", Password:"thisisasecret"}
fmt.Printf("%#v\n", user1)
user2 := User{
Name: "Aiden",
Age: 30,
Email: "[email protected]",
Password: "thisisasecret",
}
s2 := SimpleUser{}
copier.CopyWithOption(&s2, &user2, copier.Option{IgnoreEmpty: true, DeepCopy: false})
s2.Name = "Tom"
// main.SimpleUser{Name:"Tom", Age:30}
fmt.Printf("%#v\n", s2)
// main.User{Name:"Aiden", Age:30, Email:"[email protected]", Password:"thisisasecret"}
fmt.Printf("%#v\n", user2)
}