diff options
Diffstat (limited to 'struct.go')
-rw-r--r-- | struct.go | 61 |
1 files changed, 38 insertions, 23 deletions
@@ -22,24 +22,24 @@ type structIndex struct { } func indexStruct(typ reflect.Type) structIndex { - byName := make(map[string][]structField) - var byPos []string + var byPos []structField + byName := make(map[string][]int) - indexStructInner(typ, nil, byName, &byPos) + indexStructInner(typ, nil, &byPos, byName, map[reflect.Type]struct{}{}) ret := structIndex{ byName: make(map[string]int), } - for _, name := range byPos { - fields := byName[name] - delete(byName, name) - switch len(fields) { + for curPos, _field := range byPos { + name := _field.Name + fieldPoss := byName[name] + switch len(fieldPoss) { case 0: // do nothing case 1: ret.byName[name] = len(ret.byPos) - ret.byPos = append(ret.byPos, fields[0]) + ret.byPos = append(ret.byPos, _field) default: // To quote the encoding/json docs (version 1.18.4): // @@ -56,27 +56,29 @@ func indexStruct(typ reflect.Type) structIndex { // // 3) Otherwise there are multiple fields, and all are ignored; no error // occurs. - leastLevel := len(fields[0].Path) - for _, field := range fields[1:] { + leastLevel := len(byPos[fieldPoss[0]].Path) + for _, fieldPos := range fieldPoss[1:] { + field := byPos[fieldPos] if len(field.Path) < leastLevel { leastLevel = len(field.Path) } } var numUntagged, numTagged int - var untaggedIdx, taggedIdx int - for i, field := range fields { + var untaggedPos, taggedPos int + for _, fieldPos := range fieldPoss { + field := byPos[fieldPos] if len(field.Path) != leastLevel { continue } if field.Tagged { numTagged++ - taggedIdx = i + taggedPos = fieldPos if numTagged > 1 { break // optimization } } else { numUntagged++ - untaggedIdx = i + untaggedPos = fieldPos } } switch numTagged { @@ -85,12 +87,16 @@ func indexStruct(typ reflect.Type) structIndex { case 0: // do nothing case 1: - ret.byName[name] = len(ret.byPos) - ret.byPos = append(ret.byPos, fields[untaggedIdx]) + if curPos == untaggedPos { + ret.byName[name] = len(ret.byPos) + ret.byPos = append(ret.byPos, byPos[curPos]) + } } case 1: - ret.byName[name] = len(ret.byPos) - ret.byPos = append(ret.byPos, fields[taggedIdx]) + if curPos == taggedPos { + ret.byName[name] = len(ret.byPos) + ret.byPos = append(ret.byPos, byPos[curPos]) + } } } } @@ -98,7 +104,13 @@ func indexStruct(typ reflect.Type) structIndex { return ret } -func indexStructInner(typ reflect.Type, prefix []int, byName map[string][]structField, byPos *[]string) { +func indexStructInner(typ reflect.Type, prefix []int, byPos *[]structField, byName map[string][]int, seen map[reflect.Type]struct{}) { + if _, ok := seen[typ]; ok { + return + } + seen[typ] = struct{}{} + defer delete(seen, typ) + n := typ.NumField() for i := 0; i < n; i++ { path := append(append([]int(nil), prefix...), i) @@ -123,25 +135,28 @@ func indexStructInner(typ reflect.Type, prefix []int, byName map[string][]struct } tagName, opts := parseTag(tag) name := tagName + if !isValidTag(name) { + name = "" + } if name == "" { name = fTyp.Name } - if embed { + if embed && tagName == "" { t := fTyp.Type if t.Kind() == reflect.Pointer { t = t.Elem() } - indexStructInner(t, path, byName, byPos) + indexStructInner(t, path, byPos, byName, seen) } else { - byName[name] = append(byName[name], structField{ + byName[name] = append(byName[name], len(*byPos)) + *byPos = append(*byPos, structField{ Name: name, Path: path, Tagged: tagName != "", OmitEmpty: opts.Contains("omitempty"), Quote: opts.Contains("string") && isQuotable(fTyp.Type), }) - *byPos = append(*byPos, name) } } } |