Skip to content

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

sh
go get github.com/jinzhu/copier

Sử 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.

go
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.

go
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 đó UserStudent là hai struct hoàn toàn khác kiểu, không có bất kỳ liên hệ nào.

go
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

go
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

go
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

go
type TypeConverter struct {
  SrcType interface{}
  DstType interface{}
  Fn      func(src interface{}) (dst interface{}, err error)
}

Như sau

go
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

  1. 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
  2. Kiểm tra lỗi: Luôn kiểm tra lỗi trả về từ copier.Copy
  3. Tag struct: Sử dụng tag copier để tùy chỉnh hành vi sao chép
  4. Field mapping: Sử dụng FieldNameMapping để ánh xạ trường có tên khác nhau
  5. 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.

Golang by www.golangdev.cn edit