github.com/zerosnake0/jzon@v0.0.9-0.20230801092939-1b135cb83f7f/struct.go (about) 1 package jzon 2 3 import ( 4 "reflect" 5 "sort" 6 ) 7 8 /* see encoding/json 9 * - some additional comments may be added 10 * - some code may be slightly modified 11 */ 12 13 func describeStruct(st reflect.Type, tagKey string, onlyTaggedField bool) structFields { 14 // Anonymous fields to explore at the current level and the next. 15 var current []field 16 next := []field{{ 17 typ: st, 18 offsets: []offset{{ 19 val: 0, 20 }}, 21 }} 22 23 // Count of queued names for current level and the next. 24 var count, nextCount map[reflect.Type]int 25 26 // Types already visited at an earlier level. 27 visited := map[reflect.Type]bool{} 28 29 // Fields found. 30 var fields []field 31 32 for len(next) > 0 { 33 // move to next level 34 current, next = next, current[:0] 35 count, nextCount = nextCount, map[reflect.Type]int{} 36 37 for _, f := range current { 38 /* 1. First of all we can not have something like 39 * type A struct { 40 * A 41 * } 42 * or something like 43 * type A struct { type B struct { 44 * B A 45 * } } 46 * we must have something like 47 * type A struct { 48 * *A 49 * } 50 * of course there can be more embedded levels, but at least one 51 * pointer is required 52 * 53 * 2. Next, when we have a struct embedded by itself, according to the 54 * field sorting which will be applied after, the embedded one will 55 * have lower priority than the parent one, so we just skip it here 56 */ 57 if visited[f.typ] { 58 continue 59 } 60 visited[f.typ] = true 61 62 for i := 0; i < f.typ.NumField(); i++ { 63 sf := f.typ.Field(i) 64 if sf.PkgPath != "" { // the field is not exported, i.e the field begins with lowercase 65 if sf.Anonymous { // embedded field 66 t := sf.Type 67 if t.Kind() == reflect.Ptr { 68 t = t.Elem() 69 } 70 /* 71 * We can only have 72 * type A struct { 73 * *B 74 * } 75 * but not 76 * type A struct { 77 * **B 78 * } 79 * (even if the **B is defined by type aliasing) 80 */ 81 if t.Kind() != reflect.Struct { 82 continue 83 } 84 } else { // not embedded, just skip 85 continue 86 } 87 } 88 89 var ( 90 // `json:"<name>,<opts>"` 91 name string 92 opts tagOptions 93 ) 94 tag, ok := sf.Tag.Lookup(tagKey) 95 if ok { 96 if tag == "-" { 97 continue 98 } 99 name, opts = parseTag(tag) 100 if !isValidTag(name) { 101 name = "" 102 } 103 } else { 104 if onlyTaggedField && !sf.Anonymous { 105 continue 106 } 107 } 108 109 index := make([]int, len(f.index)+1) 110 copy(index, f.index) 111 index[len(f.index)] = i 112 113 ft := sf.Type 114 /* 115 * When ft.Name() == "", we may have: 116 * 1. a pointer 117 * 2. an anonymous struct like 118 * type A struct { 119 * B struct { ... } 120 * } 121 * 3. other case? 122 */ 123 if ft.Name() == "" && ft.Kind() == reflect.Ptr { 124 ft = ft.Elem() 125 } 126 127 l := len(f.offsets) 128 offsets := make([]offset, l, l+1) 129 copy(offsets, f.offsets) 130 offsets[l-1].val += sf.Offset 131 132 // Record found field and index sequence. 133 if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { 134 /* either: 135 * 1. the field has a json tag 136 * 2. the field is not embedded 137 * 3. the field type is not struct (or pointer of struct) 138 */ 139 ptrType := reflect.PtrTo(sf.Type) 140 field := field{ 141 index: index, 142 offsets: offsets, 143 typ: ft, 144 omitEmpty: opts.Contains("omitempty"), 145 146 ptrType: ptrType, 147 // rtype: rtypeOfType(ptrType), 148 } 149 150 if name == "" { 151 field.name = sf.Name 152 field.tagged = false 153 } else { 154 field.name = name 155 field.tagged = true 156 } 157 field.nameBytes = []byte(field.name) 158 field.nameBytesUpper = toUpper(field.nameBytes, nil) 159 field.equalFold = foldFunc(field.nameBytes) 160 161 // Only strings, floats, integers, and booleans can be quoted. 162 if opts.Contains("string") { 163 switch ft.Kind() { 164 case reflect.Bool, 165 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 166 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, 167 reflect.Float32, reflect.Float64, 168 reflect.String: 169 field.quoted = true 170 } 171 } 172 173 fields = append(fields, field) 174 if count[f.typ] > 1 { 175 /* when we arrived here, we are inside a level, where there are at least 176 * two embedded field with a same (final) type 177 * but the embedded field type will be analysed only once (this is ensured 178 * by both: 179 * 1. the visited map above 180 * 2. the nextCount check below 181 * 182 * but there will be multiple fields with same: 183 * 1. json field name 184 * 2. field depth 185 * 3. field tagged or not 186 * but with different: 187 * 1. index (or index array) 188 * 189 * The fields are sorted by: 190 * 1. json field name 191 * 2. depth 192 * 3. tagged or not (tagged is less) 193 * 4. the index array ([0,0,0] < [0,0,1]) 194 * 195 * For each json field name, the dominant field is selected between 196 * the two first elements with the same json field name: 197 * 1. the one with lesser depth wins, otherwise 198 * 2. the one with json tag wins, otherwise 199 * 3. there is no dominant field 200 * 201 * Hence, there will be no dominant one among these multiple similar fields. 202 * But we still add the same field once more, because in this case the embedded 203 * field with a same type and a deeper depth should also be ignored, for example: 204 * type A struct {} 205 * type B = A 206 * type C struct { A } 207 * type D struct { 208 * C 209 * B 210 * A 211 * } 212 * we can just add the same field again because they will be eliminated in the end 213 * we will only add once more because the same struct type is analysed only once 214 */ 215 fields = append(fields, field) 216 } 217 continue 218 } 219 220 nextCount[ft]++ 221 if nextCount[ft] == 1 { 222 /* one example for nextCount[ft] > 1 223 * type A struct {} 224 * type B = A 225 * type C struct { 226 * B 227 * A 228 * } 229 * in this case the name is the name of type (A) 230 */ 231 if sf.Type.Kind() == reflect.Ptr { 232 var elemRType rtype 233 if sf.PkgPath == "" { // the field is exported 234 elemRType = rtypeOfType(sf.Type.Elem()) 235 } 236 offsets = append(offsets, offset{ 237 val: 0, 238 rtype: elemRType, 239 }) 240 } 241 next = append(next, field{ 242 name: ft.Name(), 243 index: index, 244 offsets: offsets, 245 typ: ft, 246 }) 247 } 248 } 249 } 250 } 251 252 sort.Slice(fields, func(i, j int) bool { 253 x := fields 254 // sort field by name, breaking ties with depth, then 255 // breaking ties with "name came from json tag", then 256 // breaking ties with index sequence. 257 if x[i].name != x[j].name { 258 return x[i].name < x[j].name 259 } 260 if len(x[i].index) != len(x[j].index) { 261 return len(x[i].index) < len(x[j].index) 262 } 263 if x[i].tagged != x[j].tagged { 264 return x[i].tagged 265 } 266 return byIndex(x).Less(i, j) 267 }) 268 269 // Delete all fields that are hidden by the Go rules for embedded fields, 270 // except that fields with JSON tags are promoted. 271 272 // The fields are sorted in primary order of name, secondary order 273 // of field index length. Loop over names; for each name, delete 274 // hidden fields by choosing the one dominant field that survives. 275 out := fields[:0] 276 for advance, i := 0, 0; i < len(fields); i += advance { 277 // One iteration per name. 278 // Find the sequence of fields with the name of this first field. 279 fi := fields[i] 280 name := fi.name 281 for advance = 1; i+advance < len(fields); advance++ { 282 fj := fields[i+advance] 283 if fj.name != name { 284 break 285 } 286 } 287 if advance == 1 { // Only one field with this name 288 out = append(out, fi) 289 continue 290 } 291 dominant, ok := dominantField(fields[i : i+advance]) 292 if ok { 293 out = append(out, dominant) 294 } 295 } 296 297 fields = out 298 sort.Sort(byIndex(fields)) 299 300 return fields 301 }