Go每日一库之20:copier

简介

上一篇文章介绍了mergo库的使用,mergo是用来给结构体或map赋值的。mergo有一个明显的不足------它只能处理相同类型的结构!如果类型不同,即使字段名和类型完全相同,mergo也无能为力。今天我们要介绍的copier库就能处理不同类型之间的赋值。除此之外,copier还能:

  • 调用同名方法为字段赋值;
  • 以源对象字段为参数调用目标对象的方法,从而为目标对象赋值(当然也可以做其它的任何事情);
  • 将切片赋值给切片(可以是不同类型哦);
  • 将结构体追加到切片中。

感谢@thinkgos推荐。

顺带一提,作者是国人jinzhu大佬,如果你想找一个 Go 语言的 ORM 库,gorm你值得拥有!

快速使用

先安装:

bash 复制代码
$ go get github.com/jinzhu/copier

后使用:

go 复制代码
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)
  fmt.Printf("%#v\n", employee)
}

很好理解,就是将user对象中的字段赋值到employee的同名字段中。如果目标对象中没有同名的字段,则该字段被忽略。

高级特性

方法赋值

目标对象中的一些字段,源对象中没有,但是源对象有同名的方法。这时Copy会调用这个方法,将返回值赋值给目标对象中的字段:

go 复制代码
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)
  fmt.Printf("%#v\n", employee)
}

我们给User添加一个DoubleAge方法。Employee结构有字段DoubleAge,User中没有,但是User有一个同名的方法,这时Copy调用user的DoubleAge方法为employee的DoubleAge赋值,得到 36。

调用目标方法

有时候源对象中的某个字段没有出现在目标对象中,但是目标对象有一个同名的方法,方法接受一个同类型的参数,这时Copy会以源对象的这个字段作为参数调用目标对象的该方法:

go 复制代码
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)
  fmt.Printf("%#v\n", employee)
}

我们给Employee添加了一个Role方法,User的字段Role没有出现在Employee中,但是Employee有一个同名方法。Copy函数内部会以user对象的Role字段为参数调用employee的Role方法。最终,我们的employee对象的SuperRole值变为SuperAdmin。实际上,这个方法中可以执行任何操作,不一定是赋值。

切片赋值

使用一个切片来为另一个切片赋值。如果类型相同,那好办,直接append就行。如果类型不同呢?copier就派上大用场了:

go 复制代码
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)
  fmt.Printf("%#v\n", employees)
}

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

将结构赋值到切片

这个不难,实际上就是根据源对象生成一个和目标切片类型相符合的对象,然后append到目标切片中:

go 复制代码
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)
  fmt.Printf("%#v\n", employees)
}

上面代码中,Copy先通过user生成一个Employee对象,然后append到切片employees中。

汇总

最后将所有的特性汇总在一个例子中,其实就是Copier的 GitHub 仓库首页的例子:

go 复制代码
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)
  fmt.Printf("%#v\n", employee)

  copier.Copy(&employees, &user)
  fmt.Printf("%#v\n", employees)

  // employees = []Employee{}

  copier.Copy(&employees, &users)
  fmt.Printf("%#v\n", employees)
}

上面例子中,我故意把employees = []Employee{}这一行注释掉,最后输出的employees是 3 个元素,能更清楚的看出切片到切片是append的,目标切片原来的元素还是保留的。

总结

copier库的代码量很小,用了不到 200 行的代码就实现了如此实用的一个功能,非常值得一看!

大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue😄

参考

  1. copier GitHub:github.com/jinzhu/copi...
  2. Go 每日一库 GitHub:github.com/go-quiz/go-...
相关推荐
hourinX17 小时前
从 v1 到 v2,我重新拆了头像生成的整条链路—— gdenticon 重构日志(二)
go
FungLeo20 小时前
成为全栈·Go 后端篇·分类树与标签关系:目录变更为何需要协调
golang·go·gorm·数据库事务·分类树·成为全栈
FungLeo20 小时前
成为全栈·Go 后端篇·文章查询与展示投影:安全分页、排序和软删除
golang·go·gorm·分页查询·成为全栈·sql安全
KevinAshen672 天前
用 Go 修复旧下载工具:从接口 403 到凭证截断的完整复盘
go·github
FungLeo3 天前
成为全栈·Go 后端篇·跨语言认证兼容:Node 与 Go 如何互认 JWT 和密码
golang·go·jwt·身份认证·成为全栈·bcrypt
FungLeo3 天前
成为全栈·Go 后端篇·重写之前先锁基线:契约、Node 版本与行为清单
golang·node.js·go·openapi·契约测试·成为全栈
小满zs3 天前
Go语言第十七章(反射)
后端·go
小满zs3 天前
Go语言第十五章(JSON序列化)
后端·go
小满zs3 天前
Go语言第十六章(自定义类型)
后端·go
w-w0w-w3 天前
Go语言学习day1
go