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