github.com/gogf/gf/v2@v2.7.4/os/gstructs/gstructs_tag.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/gogf/gf. 6 7 package gstructs 8 9 import ( 10 "reflect" 11 "strconv" 12 13 "github.com/gogf/gf/v2/errors/gcode" 14 "github.com/gogf/gf/v2/errors/gerror" 15 "github.com/gogf/gf/v2/util/gtag" 16 ) 17 18 // ParseTag parses tag string into map. 19 // For example: 20 // ParseTag(`v:"required" p:"id" d:"1"`) => map[v:required p:id d:1]. 21 func ParseTag(tag string) map[string]string { 22 var ( 23 key string 24 data = make(map[string]string) 25 ) 26 for tag != "" { 27 // Skip leading space. 28 i := 0 29 for i < len(tag) && tag[i] == ' ' { 30 i++ 31 } 32 tag = tag[i:] 33 if tag == "" { 34 break 35 } 36 // Scan to colon. A space, a quote or a control character is a syntax error. 37 // Strictly speaking, control chars include the range [0x7f, 0x9f], not just 38 // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters 39 // as it is simpler to inspect the tag's bytes than the tag's runes. 40 i = 0 41 for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { 42 i++ 43 } 44 if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { 45 break 46 } 47 key = tag[:i] 48 tag = tag[i+1:] 49 50 // Scan quoted string to find value. 51 i = 1 52 for i < len(tag) && tag[i] != '"' { 53 if tag[i] == '\\' { 54 i++ 55 } 56 i++ 57 } 58 if i >= len(tag) { 59 break 60 } 61 quotedValue := tag[:i+1] 62 tag = tag[i+1:] 63 value, err := strconv.Unquote(quotedValue) 64 if err != nil { 65 panic(gerror.WrapCodef(gcode.CodeInvalidParameter, err, `error parsing tag "%s"`, tag)) 66 } 67 data[key] = gtag.Parse(value) 68 } 69 return data 70 } 71 72 // TagFields retrieves and returns struct tags as []Field from `pointer`. 73 // 74 // The parameter `pointer` should be type of struct/*struct. 75 // 76 // Note that, 77 // 1. It only retrieves the exported attributes with first letter upper-case from struct. 78 // 2. The parameter `priority` should be given, it only retrieves fields that has given tag. 79 func TagFields(pointer interface{}, priority []string) ([]Field, error) { 80 return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{}) 81 } 82 83 // TagMapName retrieves and returns struct tags as map[tag]attribute from `pointer`. 84 // 85 // The parameter `pointer` should be type of struct/*struct. 86 // 87 // Note that, 88 // 1. It only retrieves the exported attributes with first letter upper-case from struct. 89 // 2. The parameter `priority` should be given, it only retrieves fields that has given tag. 90 // 3. If one field has no specified tag, it uses its field name as result map key. 91 func TagMapName(pointer interface{}, priority []string) (map[string]string, error) { 92 fields, err := TagFields(pointer, priority) 93 if err != nil { 94 return nil, err 95 } 96 tagMap := make(map[string]string, len(fields)) 97 for _, field := range fields { 98 tagMap[field.TagValue] = field.Name() 99 } 100 return tagMap, nil 101 } 102 103 // TagMapField retrieves struct tags as map[tag]Field from `pointer`, and returns it. 104 // The parameter `object` should be either type of struct/*struct/[]struct/[]*struct. 105 // 106 // Note that, 107 // 1. It only retrieves the exported attributes with first letter upper-case from struct. 108 // 2. The parameter `priority` should be given, it only retrieves fields that has given tag. 109 // 3. If one field has no specified tag, it uses its field name as result map key. 110 func TagMapField(object interface{}, priority []string) (map[string]Field, error) { 111 fields, err := TagFields(object, priority) 112 if err != nil { 113 return nil, err 114 } 115 tagMap := make(map[string]Field, len(fields)) 116 for _, field := range fields { 117 tagField := field 118 tagMap[field.TagValue] = tagField 119 } 120 return tagMap, nil 121 } 122 123 func getFieldValues(structObject interface{}) ([]Field, error) { 124 var ( 125 reflectValue reflect.Value 126 reflectKind reflect.Kind 127 ) 128 if v, ok := structObject.(reflect.Value); ok { 129 reflectValue = v 130 reflectKind = reflectValue.Kind() 131 } else { 132 reflectValue = reflect.ValueOf(structObject) 133 reflectKind = reflectValue.Kind() 134 } 135 for { 136 switch reflectKind { 137 case reflect.Ptr: 138 if !reflectValue.IsValid() || reflectValue.IsNil() { 139 // If pointer is type of *struct and nil, then automatically create a temporary struct. 140 reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() 141 reflectKind = reflectValue.Kind() 142 } else { 143 reflectValue = reflectValue.Elem() 144 reflectKind = reflectValue.Kind() 145 } 146 case reflect.Array, reflect.Slice: 147 reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() 148 reflectKind = reflectValue.Kind() 149 default: 150 goto exitLoop 151 } 152 } 153 154 exitLoop: 155 for reflectKind == reflect.Ptr { 156 reflectValue = reflectValue.Elem() 157 reflectKind = reflectValue.Kind() 158 } 159 if reflectKind != reflect.Struct { 160 return nil, gerror.NewCode( 161 gcode.CodeInvalidParameter, 162 "given value should be either type of struct/*struct/[]struct/[]*struct", 163 ) 164 } 165 var ( 166 structType = reflectValue.Type() 167 length = reflectValue.NumField() 168 fields = make([]Field, length) 169 ) 170 for i := 0; i < length; i++ { 171 fields[i] = Field{ 172 Value: reflectValue.Field(i), 173 Field: structType.Field(i), 174 } 175 } 176 return fields, nil 177 } 178 179 func getFieldValuesByTagPriority( 180 pointer interface{}, priority []string, repeatedTagFilteringMap map[string]struct{}, 181 ) ([]Field, error) { 182 fields, err := getFieldValues(pointer) 183 if err != nil { 184 return nil, err 185 } 186 var ( 187 tagName string 188 tagValue string 189 tagFields = make([]Field, 0) 190 ) 191 for _, field := range fields { 192 // Only retrieve exported attributes. 193 if !field.IsExported() { 194 continue 195 } 196 tagValue = "" 197 for _, p := range priority { 198 tagName = p 199 tagValue = field.Tag(p) 200 if tagValue != "" && tagValue != "-" { 201 break 202 } 203 } 204 if tagValue != "" { 205 // Filter repeated tag. 206 if _, ok := repeatedTagFilteringMap[tagValue]; ok { 207 continue 208 } 209 tagField := field 210 tagField.TagName = tagName 211 tagField.TagValue = tagValue 212 tagFields = append(tagFields, tagField) 213 } 214 // If this is an embedded attribute, it retrieves the tags recursively. 215 if field.IsEmbedded() && field.OriginalKind() == reflect.Struct { 216 subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, repeatedTagFilteringMap) 217 if err != nil { 218 return nil, err 219 } else { 220 tagFields = append(tagFields, subTagFields...) 221 } 222 } 223 } 224 return tagFields, nil 225 }