copier
Kho lưu trữ mã nguồn mở: jinzhu/copier: Copier for golang, copy value from struct to struct and more (github.com)
Địa chỉ tài liệu: jinzhu/copier: Copier for golang, copy value from struct to struct and more (github.com)
copier là một thư viện dùng để sao chép kiểu trong Go, chủ yếu dùng để chuyển đổi giữa các struct. Tác giả cũng là người tạo ra gorm, nó có các đặc điểm sau
- Sao chép sâu
- Sao chép các trường cùng tên
- Sao chép slice
- Sao chép map
- Sao chép phương thức
Vì việc sao chép của copier dựa trên reflection nên sẽ có tổn thất về hiệu năng. Nhìn chung các thư viện sao chép kiểu này được chia thành hai loại, một loại dựa trên reflection như copier, loại còn lại dựa trên sinh mã, thông qua việc sinh mã chuyển đổi kiểu, phương pháp này không gây tổn thất hiệu năng, thư viện tương tự là jmattheis/goverter.
Cài đặt
go get github.com/jinzhu/copierSử dụng
Thư viện này sử dụng rất đơn giản nhưng lại cực kỳ hữu ích. Nó chỉ công khai hai hàm, một là copier.Copy.
func Copy(toValue interface{}, fromValue interface{}) (err error)Hàm còn lại là copier.CopyWithOption, hàm sau có thể tùy chỉnh một số cấu hình cho hành vi sao chép, mặc định sẽ không thực hiện sao chép sâu.
type Option struct {
IgnoreEmpty bool
CaseSensitive bool
DeepCopy bool
FieldNameMapping []FieldNameMapping
}
func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err error)Dưới đây là ví dụ về chuyển đổi struct khác kiểu, trong đó User và Student là hai struct hoàn toàn khác kiểu, không có bất kỳ liên hệ nào.
type User struct {
Id string
Name string
// Khi là struct đích, bỏ qua trường này
Address string `copier:"-"`
}
type Student struct {
// Chỉ định tên trường
StudentId string `copier:"Id"`
StudentName string `copier:"Name"`
Address string
School string
Class string
}
func main() {
student := Student{
StudentId: "123",
StudentName: "jack",
Address: "usa",
School: "MIT",
Class: "AI",
}
user := User{}
if err := copier.Copy(&user, &student); err != nil {
panic(err)
}
fmt.Printf("%+v\n", student)
fmt.Printf("%+v\n", user)
}Đầu ra
{StudentId:123 StudentName:jack Address:usa School:MIT Class:AI}
{Id:123 Name:jack Address:}Xem tiếp sao chép slice
func main() {
student := []Student{
{
StudentId: "123",
StudentName: "jack",
Address: "usa",
School: "MIT",
Class: "AI",
},
{
StudentId: "123",
StudentName: "jack",
Address: "usa",
School: "MIT",
Class: "AI",
},
}
var user []User
if err := copier.Copy(&user, &student); err != nil {
panic(err)
}
fmt.Printf("%+v\n", student)
fmt.Printf("%+v\n", user)
}Đầu ra
[{StudentId:123 StudentName:jack Address:usa School:MIT Class:AI} {StudentId:123 StudentName:jack Address:usa School:MIT Class:AI}]
[{Id:123 Name:jack Address:} {Id:123 Name:jack Address:}]Sao chép map
type User struct {
Id string
Name string
// Khi là struct đích, bỏ qua trường này
Address string `copier:"-"`
}
type Student struct {
// Chỉ định tên trường
StudentId string `copier:"Id"`
StudentName string `copier:"Name"`
Address string
School string
Class string
}
func main() {
student := Student{
StudentId: "123",
StudentName: "jack",
Address: "usa",
School: "MIT",
Class: "AI",
}
src := make(map[string]Student)
src["a"] = student
src["b"] = student
dest := make(map[string]User)
if err := copier.Copy(&dest, &src); err != nil {
panic(err)
}
fmt.Printf("%+v\n", src)
fmt.Printf("%+v\n", dest)
}Đầu ra
map[a:{StudentId:123 StudentName:jack Address:usa School:MIT Class:AI} b:{StudentId:123 StudentName:jack Address:usa School:MIT Class:AI}]
map[a:{Id:123 Name:jack Address:} b:{Id:123 Name:jack Address:}]Tùy chỉnh
Cũng có thể tùy chỉnh phương thức chuyển đổi, chỉ cần truyền vào copier.TypeConverter
type TypeConverter struct {
SrcType interface{}
DstType interface{}
Fn func(src interface{}) (dst interface{}, err error)
}Như sau
type User struct {
Id string
Name string
// Khi là struct đích, bỏ qua trường này
Address string `copier:"-"`
}
type Student struct {
// Chỉ định tên trường
StudentId string `copier:"Id"`
StudentName string `copier:"Name"`
Address string
School string
Class string
}
func main() {
student := Student{
StudentId: "123",
StudentName: "jack",
Address: "usa",
School: "MIT",
Class: "AI",
}
src := make(map[string]Student)
src["a"] = student
src["b"] = student
dest := make(map[string]User)
if err := copier.CopyWithOption(&dest, &src, copier.Option{
IgnoreEmpty: false,
CaseSensitive: false,
DeepCopy: false,
Converters: []copier.TypeConverter{
{
SrcType: Student{},
DstType: User{},
Fn: func(src interface{}) (dst interface{}, err error) {
s, ok := src.(Student)
if !ok {
return User{}, errors.New("error type")
}
return User{
Id: s.StudentId,
}, nil
},
},
},
FieldNameMapping: nil,
}); err != nil {
panic(err)
}
fmt.Printf("%+v\n", src)
fmt.Printf("%+v\n", dest)
}Đầu ra
map[a:{StudentId:123 StudentName:jack Address:usa School:MIT Class:AI} b:{StudentId:123 StudentName:jack Address:usa School:MIT Class:AI}]
map[a:{Id:123 Name: Address:} b:{Id:123 Name: Address:}]Best Practices
- Sử dụng DeepCopy cẩn thận: Chỉ sử dụng sao chép sâu khi thực sự cần thiết vì ảnh hưởng đến hiệu năng
- Kiểm tra lỗi: Luôn kiểm tra lỗi trả về từ copier.Copy
- Tag struct: Sử dụng tag
copierđể tùy chỉnh hành vi sao chép - Field mapping: Sử dụng FieldNameMapping để ánh xạ trường có tên khác nhau
- Performance: Xem xét sử dụng thư viện sinh mã nếu cần hiệu năng cao
Kết luận
copier là một thư viện hữu ích cho việc sao chép và chuyển đổi dữ liệu giữa các struct trong Go. Với cú pháp đơn giản và các tùy chọn linh hoạt, copier giúp giảm thiểu code boilerplate khi làm việc với các kiểu dữ liệu khác nhau.
