struct_copy.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // Copyright 2019 github.com. All rights reserved.
  2. // Use of this source code is governed by github.com.
  3. package utils
  4. import (
  5. "errors"
  6. "reflect"
  7. )
  8. // check 检查参数
  9. func check(data interface{}) error {
  10. if reflect.TypeOf(data).Kind() != reflect.Ptr {
  11. return errors.New("param must be ptr")
  12. }
  13. if reflect.ValueOf(data).IsNil() {
  14. return errors.New("param can not be nil")
  15. }
  16. if reflect.TypeOf(data).Elem().Kind() == reflect.Ptr {
  17. return errors.New("param must be ptr")
  18. }
  19. return nil
  20. }
  21. // getFieldArray 获取结构体元素数组
  22. func getFieldArray(t reflect.Type) (ret []reflect.StructField) {
  23. for i := 0; i < t.NumField(); i++ {
  24. field := t.Field(i)
  25. ret = append(ret, field)
  26. }
  27. return ret
  28. }
  29. // FieldByTag 根据tag名(tagName)和tag值(tag)从结构体重获取元素
  30. // 如果tag或tagName为空,则根据元素名称获取元素
  31. func FieldByTag(value reflect.Value, name string, tag string, tagName string) reflect.Value {
  32. if tag == "" || tagName == "" {
  33. return value.FieldByName(name)
  34. }
  35. itype := value.Type()
  36. for i := 0; i < itype.NumField(); i++ {
  37. v := itype.Field(i)
  38. if v.Tag.Get(tagName) == tag {
  39. return value.FieldByIndex(v.Index)
  40. }
  41. }
  42. return reflect.Value{}
  43. }
  44. // subStructCopy copy结构体中的子结构体
  45. func subStructCopy(dst reflect.Value, src reflect.Value, tagName string) {
  46. fields := getFieldArray(src.Type())
  47. for _, v := range fields {
  48. // 获取元素
  49. srcf := FieldByTag(src, v.Name, v.Tag.Get(tagName), tagName)
  50. dstf := FieldByTag(dst, v.Name, v.Tag.Get(tagName), tagName)
  51. // 是否能赋值
  52. if dstf.CanSet() == false {
  53. continue
  54. }
  55. // 如果元素是结构体,递归
  56. if v.Type.Kind() == reflect.Struct {
  57. subStructCopy(dstf, srcf, tagName)
  58. continue
  59. }
  60. if dstf.Type() != srcf.Type() {
  61. // 源和目标类型不一致,基础类型为切片且元素都为结构体,进行切片拷贝
  62. if v.Type.Kind() == reflect.Slice &&
  63. dstf.Type().Kind() == reflect.Slice &&
  64. srcf.Type().Kind() == reflect.Slice &&
  65. dstf.Type().Elem().Kind() == reflect.Struct &&
  66. srcf.Type().Elem().Kind() == reflect.Struct {
  67. array := reflect.MakeSlice(dstf.Type(), 0, 0)
  68. for i := 0; i < srcf.Len(); i++ {
  69. item := reflect.New(dstf.Type().Elem())
  70. subStructCopy(item.Elem(), srcf.Index(i), tagName)
  71. array = reflect.Append(array, item.Elem())
  72. }
  73. dstf.Set(array)
  74. }
  75. continue
  76. }
  77. // 拷贝指针指向的数据
  78. if dstf.Kind() == reflect.Ptr && srcf.IsNil() == false {
  79. dstf.Set(reflect.New(dstf.Type().Elem()))
  80. dstf.Elem().Set(srcf.Elem())
  81. continue
  82. }
  83. // 普通数据拷贝
  84. dstf.Set(srcf)
  85. }
  86. }
  87. // StructCopy 根据tagName(如json)进行结构体拷贝
  88. // tagName为空,默认按结构体元素名进行拷贝
  89. func StructCopy(dst interface{}, src interface{}, tagName string) error {
  90. if err := check(dst); err != nil {
  91. return err
  92. }
  93. if err := check(src); err != nil {
  94. return err
  95. }
  96. srcv := reflect.ValueOf(src).Elem()
  97. dstv := reflect.ValueOf(dst).Elem()
  98. subStructCopy(dstv, srcv, tagName)
  99. return nil
  100. }