Go Copier简介
本文主要介绍 jinzhu/copier 的使用和常用场景
简介
在go后端项目开发中,内部rpc服务返回的字段跟api服务相差无几,一个个赋值比较费事儿。那么就需要Object、List、HashMap 等进行值拷贝。jinzhu/copier即提供了这些场景的支持。
copier 特性:
- 从方法复制到具有相同名称的字段
- 从字段复制到具有相同名称的方法
- 从一个切片复制到另一个切片
- 从结构体复制到切片
- 从map复制到map
- 强制复制带有标记的字段
- 忽略带有标记的字段
- 深拷贝
Usage
Copy from struct
package main
import (
"github.com/jinzhu/copier"
"testing"
)
type User struct {
Name string
Role string
Age int32
EmployeeCode int64 `copier:"EmployeeNum"` // specify field name
// Explicitly ignored in the destination struct.
Salary int
}
//目标结构体中的标签提供了copy指令。复制忽略
//或强制复制,如果字段没有被复制则惊慌或返回错误。
type Employee struct {
//告诉copier。如果没有复制此字段,则复制到panic。
Name string `copier:"must"`
//告诉copier。 如果没有复制此字段,则返回错误。
Age int32 `copier:"must,nopanic"`
// 告诉copier。 显式忽略复制此字段。
Salary int `copier:"-"`
DoubleAge int32
EmployeeId int64 `copier:"EmployeeNum"` // 指定字段名
SuperRole string
}
func TestCopyStruct(t *testing.T) {
var (
user = User{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 200000}
employee = Employee{Salary: 150000}
)
copier.Copy(&employee, &user)
t.Logf("%#v \n", employee)
}
output:
copier_test.go:47: main.Employee{Name:"Jinzhu", Age:18, Salary:150000, DoubleAge:36, EmployeeId:0, SuperRole:"Super Admin"}
Copy from slice to slice
func TestCopySlice(t *testing.T) {
var (
users = []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 100000}, {Name: "jinzhu 2", Age: 30, Role: "Dev", Salary: 60000}}
employees = []Employee{}
)
employees = []Employee{}
copier.Copy(&employees, &users)
t.Logf("%#v \n", employees)
}`
output :
copier_test.go:57: []main.Employee{main.Employee{Name:"Jinzhu", Age:18, Salary:0, DoubleAge:36, EmployeeId:0, SuperRole:"Super Admin"}, main.Employee{Name:"jinzhu 2", Age:30, Salary:0, DoubleAge:60, EmployeeId:0, SuperRole:"Super Dev"}}
Copy from Map to Map
func TestCopyMap(t *testing.T) {
// Copy map to map
map1 := map[int]int{3: 6, 4: 8}
map2 := map[int32]int8{}
copier.Copy(&map2, map1)
t.Logf("%#v \n", map2)
}
output :
copier_test.go:66: map[int32]int8{3:6, 4:8}
场景 1(rpc&api)
实际开发中,免不了服务间通讯,比较前文所说的场景,一个内部的rpc服务返回的参数和api服务差不多,那么就可以使用copier。
//伪代码如下
func ApiLogin(ctx context.Context,request *api.LoginRequest)(reply *api.LogingReply,err error) {
grpcClient := v1.NewGameGrpcClient(ctx)
reply, err := client.Login(ctx, &grpc.api.LoginRequest{
})
user := api.LogingReply.User{}
copier.Copy(&user, reply.User())
return &api.LoginReply{
User:user,
},err
场景 2 (model-object/aggregate)
实际开发中,不管是mvc\ddd 都会有从model到object/aggreate的repository,那么就可以使用copier。
func (r *UserRepo) Get(ctx context.Context, uid int64) (u User,err error) {
model, err := db.User.Get(ctx, uid)
if err != nil {
return
}
obj:= User{}
copy(&obj,model)
return obj,nil
}
小结
copier提供不同类型之间相同的字段名,使用tag或者方法支持不同的字段名的赋值。减少一些重复的工作量,小巧实用。