gconv_structs.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package gconv
  7. import (
  8. "github.com/gogf/gf/errors/gcode"
  9. "github.com/gogf/gf/errors/gerror"
  10. "github.com/gogf/gf/internal/json"
  11. "reflect"
  12. )
  13. // Structs converts any slice to given struct slice.
  14. // Also see Scan, Struct.
  15. func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
  16. return Scan(params, pointer, mapping...)
  17. }
  18. // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the
  19. // specified tags for `params` key-value items to struct attribute names mapping.
  20. // The parameter `priorityTag` supports multiple tags that can be joined with char ','.
  21. func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
  22. return doStructs(params, pointer, nil, priorityTag)
  23. }
  24. // StructsDeep converts any slice to given struct slice recursively.
  25. // Deprecated, use Structs instead.
  26. func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
  27. var keyToAttributeNameMapping map[string]string
  28. if len(mapping) > 0 {
  29. keyToAttributeNameMapping = mapping[0]
  30. }
  31. return doStructs(params, pointer, keyToAttributeNameMapping, "")
  32. }
  33. // doStructs converts any slice to given struct slice.
  34. //
  35. // It automatically checks and converts json string to []map if `params` is string/[]byte.
  36. //
  37. // The parameter `pointer` should be type of pointer to slice of struct.
  38. // Note that if `pointer` is a pointer to another pointer of type of slice of struct,
  39. // it will create the struct/pointer internally.
  40. func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
  41. if params == nil {
  42. // If `params` is nil, no conversion.
  43. return nil
  44. }
  45. if pointer == nil {
  46. return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
  47. }
  48. if doStructsByDirectReflectSet(params, pointer) {
  49. return nil
  50. }
  51. defer func() {
  52. // Catch the panic, especially the reflect operation panics.
  53. if exception := recover(); exception != nil {
  54. if e, ok := exception.(errorStack); ok {
  55. err = e
  56. } else {
  57. err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception)
  58. }
  59. }
  60. }()
  61. // If given `params` is JSON, it then uses json.Unmarshal doing the converting.
  62. switch r := params.(type) {
  63. case []byte:
  64. if json.Valid(r) {
  65. if rv, ok := pointer.(reflect.Value); ok {
  66. if rv.Kind() == reflect.Ptr {
  67. return json.UnmarshalUseNumber(r, rv.Interface())
  68. }
  69. } else {
  70. return json.UnmarshalUseNumber(r, pointer)
  71. }
  72. }
  73. case string:
  74. if paramsBytes := []byte(r); json.Valid(paramsBytes) {
  75. if rv, ok := pointer.(reflect.Value); ok {
  76. if rv.Kind() == reflect.Ptr {
  77. return json.UnmarshalUseNumber(paramsBytes, rv.Interface())
  78. }
  79. } else {
  80. return json.UnmarshalUseNumber(paramsBytes, pointer)
  81. }
  82. }
  83. }
  84. // Pointer type check.
  85. pointerRv, ok := pointer.(reflect.Value)
  86. if !ok {
  87. pointerRv = reflect.ValueOf(pointer)
  88. if kind := pointerRv.Kind(); kind != reflect.Ptr {
  89. return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of pointer, but got: %v", kind)
  90. }
  91. }
  92. // Converting `params` to map slice.
  93. var (
  94. paramsList []interface{}
  95. paramsRv = reflect.ValueOf(params)
  96. paramsKind = paramsRv.Kind()
  97. )
  98. for paramsKind == reflect.Ptr {
  99. paramsRv = paramsRv.Elem()
  100. paramsKind = paramsRv.Kind()
  101. }
  102. switch paramsKind {
  103. case reflect.Slice, reflect.Array:
  104. paramsList = make([]interface{}, paramsRv.Len())
  105. for i := 0; i < paramsRv.Len(); i++ {
  106. paramsList[i] = paramsRv.Index(i)
  107. }
  108. default:
  109. var paramsMaps = Maps(params)
  110. paramsList = make([]interface{}, len(paramsMaps))
  111. for i := 0; i < len(paramsMaps); i++ {
  112. paramsList[i] = paramsMaps[i]
  113. }
  114. }
  115. // If `params` is an empty slice, no conversion.
  116. if len(paramsList) == 0 {
  117. return nil
  118. }
  119. var (
  120. reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList))
  121. itemType = reflectElemArray.Index(0).Type()
  122. itemTypeKind = itemType.Kind()
  123. pointerRvElem = pointerRv.Elem()
  124. pointerRvLength = pointerRvElem.Len()
  125. )
  126. if itemTypeKind == reflect.Ptr {
  127. // Pointer element.
  128. for i := 0; i < len(paramsList); i++ {
  129. var tempReflectValue reflect.Value
  130. if i < pointerRvLength {
  131. // Might be nil.
  132. tempReflectValue = pointerRvElem.Index(i).Elem()
  133. }
  134. if !tempReflectValue.IsValid() {
  135. tempReflectValue = reflect.New(itemType.Elem()).Elem()
  136. }
  137. if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil {
  138. return err
  139. }
  140. reflectElemArray.Index(i).Set(tempReflectValue.Addr())
  141. }
  142. } else {
  143. // Struct element.
  144. for i := 0; i < len(paramsList); i++ {
  145. var tempReflectValue reflect.Value
  146. if i < pointerRvLength {
  147. tempReflectValue = pointerRvElem.Index(i)
  148. } else {
  149. tempReflectValue = reflect.New(itemType).Elem()
  150. }
  151. if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil {
  152. return err
  153. }
  154. reflectElemArray.Index(i).Set(tempReflectValue)
  155. }
  156. }
  157. pointerRv.Elem().Set(reflectElemArray)
  158. return nil
  159. }
  160. // doStructsByDirectReflectSet do the converting directly using reflect Set.
  161. // It returns true if success, or else false.
  162. func doStructsByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) {
  163. v1 := reflect.ValueOf(pointer)
  164. v2 := reflect.ValueOf(params)
  165. if v1.Kind() == reflect.Ptr {
  166. if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() {
  167. elem.Set(v2)
  168. ok = true
  169. }
  170. }
  171. return ok
  172. }