Go语言复制库jinzhu/copier

Go语言复制库jinzhu/copier

关于复制或深度克隆,Copier是一个很方便的工具。

当处理复杂的数据结构或嵌套结构时,手动复制数据可能会是一个繁琐且容易出错的任务。

Copier 在这种情况下非常有用。

Copier提供了一系列功能。

  • 从相同名称的字段复制到字段
  • 从具有相同名称的方法复制到字段
  • 从字段复制到具有相同名称的方法
  • 从切片复制到切片
  • 从结构体复制到切片
  • 从 map 复制到 map
  • 强制复制带有标记的字段
  • 忽略带有标记的字段
  • 深度复制

GitHub地址:https://github.com/jinzhu/copier

官方文档:https://pkg.go.dev/github.com/jinzhu/copier

1、安装

go get github.com/jinzhu/copier

2、官网综合例子

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)
}

3、高级特性

3.1 方法赋值

目标对象中的一些字段,源对象中没有,但是源对象有同名的方法。这时 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。

3.2 调用目标方法

有时候源对象中的某个字段没有出现在目标对象中,但是目标对象有一个同名的方法,方法接受一个同类型的参

数,这时 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。实际上,这个方法中可以执行任何操作,不一定是赋值。

3.3 切片赋值

使用一个切片来为另一个切片赋值。如果类型相同,那好办,直接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)
}

这个实际上就是将源切片中每个元素分别赋值到目标切片中。

3.4 将结构赋值到切片

这个不难,实际上就是根据源对象生成一个和目标切片类型相符合的对象,然后 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中。

3.5 结构体复制

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)
}

3.6 汇总

最后将所有的特性汇总在一个例子中,其实就是 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))
}

3.7 带选项复制

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)
}

你可能感兴趣的:(golang,golang)