You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

88 lines
1.8 KiB

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