github.com/vipernet-xyz/tm@v0.34.24/libs/json/structs.go (about)

     1  package json
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"unicode"
     8  
     9  	tmsync "github.com/vipernet-xyz/tm/libs/sync"
    10  )
    11  
    12  var (
    13  	// cache caches struct info.
    14  	cache = newStructInfoCache()
    15  )
    16  
    17  // structCache is a cache of struct info.
    18  type structInfoCache struct {
    19  	tmsync.RWMutex
    20  	structInfos map[reflect.Type]*structInfo
    21  }
    22  
    23  func newStructInfoCache() *structInfoCache {
    24  	return &structInfoCache{
    25  		structInfos: make(map[reflect.Type]*structInfo),
    26  	}
    27  }
    28  
    29  func (c *structInfoCache) get(rt reflect.Type) *structInfo {
    30  	c.RLock()
    31  	defer c.RUnlock()
    32  	return c.structInfos[rt]
    33  }
    34  
    35  func (c *structInfoCache) set(rt reflect.Type, sInfo *structInfo) {
    36  	c.Lock()
    37  	defer c.Unlock()
    38  	c.structInfos[rt] = sInfo
    39  }
    40  
    41  // structInfo contains JSON info for a struct.
    42  type structInfo struct {
    43  	fields []*fieldInfo
    44  }
    45  
    46  // fieldInfo contains JSON info for a struct field.
    47  type fieldInfo struct {
    48  	jsonName  string
    49  	omitEmpty bool
    50  	hidden    bool
    51  }
    52  
    53  // makeStructInfo generates structInfo for a struct as a reflect.Value.
    54  func makeStructInfo(rt reflect.Type) *structInfo {
    55  	if rt.Kind() != reflect.Struct {
    56  		panic(fmt.Sprintf("can't make struct info for non-struct value %v", rt))
    57  	}
    58  	if sInfo := cache.get(rt); sInfo != nil {
    59  		return sInfo
    60  	}
    61  	fields := make([]*fieldInfo, 0, rt.NumField())
    62  	for i := 0; i < cap(fields); i++ {
    63  		frt := rt.Field(i)
    64  		fInfo := &fieldInfo{
    65  			jsonName:  frt.Name,
    66  			omitEmpty: false,
    67  			hidden:    frt.Name == "" || !unicode.IsUpper(rune(frt.Name[0])),
    68  		}
    69  		o := frt.Tag.Get("json")
    70  		if o == "-" {
    71  			fInfo.hidden = true
    72  		} else if o != "" {
    73  			opts := strings.Split(o, ",")
    74  			if opts[0] != "" {
    75  				fInfo.jsonName = opts[0]
    76  			}
    77  			for _, o := range opts[1:] {
    78  				if o == "omitempty" {
    79  					fInfo.omitEmpty = true
    80  				}
    81  			}
    82  		}
    83  		fields = append(fields, fInfo)
    84  	}
    85  	sInfo := &structInfo{fields: fields}
    86  	cache.set(rt, sInfo)
    87  	return sInfo
    88  }