gstructs.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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 gstructs provides functions for struct information retrieving.
  7. package gstructs
  8. import (
  9. "reflect"
  10. "github.com/gogf/gf/v2/errors/gerror"
  11. )
  12. // Type wraps reflect.Type for additional features.
  13. type Type struct {
  14. reflect.Type
  15. }
  16. // Field contains information of a struct field .
  17. type Field struct {
  18. Value reflect.Value // The underlying value of the field.
  19. Field reflect.StructField // The underlying field of the field.
  20. // Retrieved tag name. It depends TagValue.
  21. TagName string
  22. // Retrieved tag value.
  23. // There might be more than one tags in the field,
  24. // but only one can be retrieved according to calling function rules.
  25. TagValue string
  26. }
  27. // FieldsInput is the input parameter struct type for function Fields.
  28. type FieldsInput struct {
  29. // Pointer should be type of struct/*struct.
  30. // TODO this attribute name is not suitable, which would make confuse.
  31. Pointer interface{}
  32. // RecursiveOption specifies the way retrieving the fields recursively if the attribute
  33. // is an embedded struct. It is RecursiveOptionNone in default.
  34. RecursiveOption RecursiveOption
  35. }
  36. // FieldMapInput is the input parameter struct type for function FieldMap.
  37. type FieldMapInput struct {
  38. // Pointer should be type of struct/*struct.
  39. // TODO this attribute name is not suitable, which would make confuse.
  40. Pointer interface{}
  41. // PriorityTagArray specifies the priority tag array for retrieving from high to low.
  42. // If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
  43. PriorityTagArray []string
  44. // RecursiveOption specifies the way retrieving the fields recursively if the attribute
  45. // is an embedded struct. It is RecursiveOptionNone in default.
  46. RecursiveOption RecursiveOption
  47. }
  48. type RecursiveOption int
  49. const (
  50. RecursiveOptionNone RecursiveOption = 0 // No recursively retrieving fields as map if the field is an embedded struct.
  51. RecursiveOptionEmbedded RecursiveOption = 1 // Recursively retrieving fields as map if the field is an embedded struct.
  52. RecursiveOptionEmbeddedNoTag RecursiveOption = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag.
  53. )
  54. // Fields retrieves and returns the fields of `pointer` as slice.
  55. func Fields(in FieldsInput) ([]Field, error) {
  56. var (
  57. ok bool
  58. fieldFilterMap = make(map[string]struct{})
  59. retrievedFields = make([]Field, 0)
  60. currentLevelFieldMap = make(map[string]Field)
  61. rangeFields, err = getFieldValues(in.Pointer)
  62. )
  63. if err != nil {
  64. return nil, err
  65. }
  66. for index := 0; index < len(rangeFields); index++ {
  67. field := rangeFields[index]
  68. currentLevelFieldMap[field.Name()] = field
  69. }
  70. for index := 0; index < len(rangeFields); index++ {
  71. field := rangeFields[index]
  72. if _, ok = fieldFilterMap[field.Name()]; ok {
  73. continue
  74. }
  75. if field.IsEmbedded() {
  76. if in.RecursiveOption != RecursiveOptionNone {
  77. switch in.RecursiveOption {
  78. case RecursiveOptionEmbeddedNoTag:
  79. if field.TagStr() != "" {
  80. break
  81. }
  82. fallthrough
  83. case RecursiveOptionEmbedded:
  84. structFields, err := Fields(FieldsInput{
  85. Pointer: field.Value,
  86. RecursiveOption: in.RecursiveOption,
  87. })
  88. if err != nil {
  89. return nil, err
  90. }
  91. // The current level fields can overwrite the sub-struct fields with the same name.
  92. for i := 0; i < len(structFields); i++ {
  93. var (
  94. structField = structFields[i]
  95. fieldName = structField.Name()
  96. )
  97. if _, ok = fieldFilterMap[fieldName]; ok {
  98. continue
  99. }
  100. fieldFilterMap[fieldName] = struct{}{}
  101. if v, ok := currentLevelFieldMap[fieldName]; !ok {
  102. retrievedFields = append(retrievedFields, structField)
  103. } else {
  104. retrievedFields = append(retrievedFields, v)
  105. }
  106. }
  107. continue
  108. }
  109. }
  110. continue
  111. }
  112. fieldFilterMap[field.Name()] = struct{}{}
  113. retrievedFields = append(retrievedFields, field)
  114. }
  115. return retrievedFields, nil
  116. }
  117. // FieldMap retrieves and returns struct field as map[name/tag]Field from `pointer`.
  118. //
  119. // The parameter `pointer` should be type of struct/*struct.
  120. //
  121. // The parameter `priority` specifies the priority tag array for retrieving from high to low.
  122. // If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
  123. //
  124. // The parameter `recursive` specifies whether retrieving the fields recursively if the attribute
  125. // is an embedded struct.
  126. //
  127. // Note that it only retrieves the exported attributes with first letter upper-case from struct.
  128. func FieldMap(in FieldMapInput) (map[string]Field, error) {
  129. fields, err := getFieldValues(in.Pointer)
  130. if err != nil {
  131. return nil, err
  132. }
  133. var (
  134. tagValue string
  135. mapField = make(map[string]Field)
  136. )
  137. for _, field := range fields {
  138. // Only retrieve exported attributes.
  139. if !field.IsExported() {
  140. continue
  141. }
  142. tagValue = ""
  143. for _, p := range in.PriorityTagArray {
  144. tagValue = field.Tag(p)
  145. if tagValue != "" && tagValue != "-" {
  146. break
  147. }
  148. }
  149. tempField := field
  150. tempField.TagValue = tagValue
  151. if tagValue != "" {
  152. mapField[tagValue] = tempField
  153. } else {
  154. if in.RecursiveOption != RecursiveOptionNone && field.IsEmbedded() {
  155. switch in.RecursiveOption {
  156. case RecursiveOptionEmbeddedNoTag:
  157. if field.TagStr() != "" {
  158. mapField[field.Name()] = tempField
  159. break
  160. }
  161. fallthrough
  162. case RecursiveOptionEmbedded:
  163. m, err := FieldMap(FieldMapInput{
  164. Pointer: field.Value,
  165. PriorityTagArray: in.PriorityTagArray,
  166. RecursiveOption: in.RecursiveOption,
  167. })
  168. if err != nil {
  169. return nil, err
  170. }
  171. for k, v := range m {
  172. if _, ok := mapField[k]; !ok {
  173. tempV := v
  174. mapField[k] = tempV
  175. }
  176. }
  177. }
  178. } else {
  179. mapField[field.Name()] = tempField
  180. }
  181. }
  182. }
  183. return mapField, nil
  184. }
  185. // StructType retrieves and returns the struct Type of specified struct/*struct.
  186. // The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
  187. func StructType(object interface{}) (*Type, error) {
  188. var (
  189. reflectValue reflect.Value
  190. reflectKind reflect.Kind
  191. reflectType reflect.Type
  192. )
  193. if rv, ok := object.(reflect.Value); ok {
  194. reflectValue = rv
  195. } else {
  196. reflectValue = reflect.ValueOf(object)
  197. }
  198. reflectKind = reflectValue.Kind()
  199. for {
  200. switch reflectKind {
  201. case reflect.Ptr:
  202. if !reflectValue.IsValid() || reflectValue.IsNil() {
  203. // If pointer is type of *struct and nil, then automatically create a temporary struct.
  204. reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
  205. reflectKind = reflectValue.Kind()
  206. } else {
  207. reflectValue = reflectValue.Elem()
  208. reflectKind = reflectValue.Kind()
  209. }
  210. case reflect.Array, reflect.Slice:
  211. reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
  212. reflectKind = reflectValue.Kind()
  213. default:
  214. goto exitLoop
  215. }
  216. }
  217. exitLoop:
  218. if reflectKind != reflect.Struct {
  219. return nil, gerror.Newf(
  220. `invalid object kind "%s", kind of "struct" is required`,
  221. reflectKind,
  222. )
  223. }
  224. reflectType = reflectValue.Type()
  225. return &Type{
  226. Type: reflectType,
  227. }, nil
  228. }