|
|
- package json
-
- import (
- "fmt"
- "reflect"
- "strings"
- "sync"
- "unicode"
- )
-
- var (
- // cache caches struct info.
- cache = newStructInfoCache()
- )
-
- // structCache is a cache of struct info.
- type structInfoCache struct {
- sync.RWMutex
- structInfos map[reflect.Type]*structInfo
- }
-
- func newStructInfoCache() *structInfoCache {
- return &structInfoCache{
- structInfos: make(map[reflect.Type]*structInfo),
- }
- }
-
- func (c *structInfoCache) get(rt reflect.Type) *structInfo {
- c.RLock()
- defer c.RUnlock()
- return c.structInfos[rt]
- }
-
- func (c *structInfoCache) set(rt reflect.Type, sInfo *structInfo) {
- c.Lock()
- defer c.Unlock()
- c.structInfos[rt] = sInfo
- }
-
- // structInfo contains JSON info for a struct.
- type structInfo struct {
- fields []*fieldInfo
- }
-
- // fieldInfo contains JSON info for a struct field.
- type fieldInfo struct {
- jsonName string
- omitEmpty bool
- hidden bool
- }
-
- // makeStructInfo generates structInfo for a struct as a reflect.Value.
- func makeStructInfo(rt reflect.Type) *structInfo {
- if rt.Kind() != reflect.Struct {
- panic(fmt.Sprintf("can't make struct info for non-struct value %v", rt))
- }
- if sInfo := cache.get(rt); sInfo != nil {
- return sInfo
- }
- fields := make([]*fieldInfo, 0, rt.NumField())
- for i := 0; i < cap(fields); i++ {
- frt := rt.Field(i)
- fInfo := &fieldInfo{
- jsonName: frt.Name,
- omitEmpty: false,
- hidden: frt.Name == "" || !unicode.IsUpper(rune(frt.Name[0])),
- }
- o := frt.Tag.Get("json")
- if o == "-" {
- fInfo.hidden = true
- } else if o != "" {
- opts := strings.Split(o, ",")
- if opts[0] != "" {
- fInfo.jsonName = opts[0]
- }
- for _, o := range opts[1:] {
- if o == "omitempty" {
- fInfo.omitEmpty = true
- }
- }
- }
- fields = append(fields, fInfo)
- }
- sInfo := &structInfo{fields: fields}
- cache.set(rt, sInfo)
- return sInfo
- }
|