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  }