gproperties.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. // Package gproperties provides accessing and converting for .properties content.
  7. package gproperties
  8. import (
  9. "bytes"
  10. "sort"
  11. "strings"
  12. "github.com/magiconair/properties"
  13. "github.com/gogf/gf/v2/errors/gerror"
  14. "github.com/gogf/gf/v2/internal/json"
  15. "github.com/gogf/gf/v2/util/gconv"
  16. )
  17. // Decode converts properties format to map.
  18. func Decode(data []byte) (res map[string]interface{}, err error) {
  19. res = make(map[string]interface{})
  20. pr, err := properties.Load(data, properties.UTF8)
  21. if err != nil || pr == nil {
  22. err = gerror.Wrapf(err, `Lib magiconair load Properties data failed.`)
  23. return nil, err
  24. }
  25. for _, key := range pr.Keys() {
  26. // ignore existence check: we know it's there
  27. value, _ := pr.Get(key)
  28. // recursively build nested maps
  29. path := strings.Split(key, ".")
  30. lastKey := strings.ToLower(path[len(path)-1])
  31. deepestMap := deepSearch(res, path[0:len(path)-1])
  32. // set innermost value
  33. deepestMap[lastKey] = value
  34. }
  35. return res, nil
  36. }
  37. // Encode converts map to properties format.
  38. func Encode(data map[string]interface{}) (res []byte, err error) {
  39. pr := properties.NewProperties()
  40. flattened := map[string]interface{}{}
  41. flattened = flattenAndMergeMap(flattened, data, "", ".")
  42. keys := make([]string, 0, len(flattened))
  43. for key := range flattened {
  44. keys = append(keys, key)
  45. }
  46. sort.Strings(keys)
  47. for _, key := range keys {
  48. _, _, err := pr.Set(key, gconv.String(flattened[key]))
  49. if err != nil {
  50. err = gerror.Wrapf(err, `Sets the property key to the corresponding value failed.`)
  51. return nil, err
  52. }
  53. }
  54. var buf bytes.Buffer
  55. _, err = pr.Write(&buf, properties.UTF8)
  56. if err != nil {
  57. err = gerror.Wrapf(err, `Properties Write buf failed.`)
  58. return nil, err
  59. }
  60. return buf.Bytes(), nil
  61. }
  62. // ToJson convert .properties format to JSON.
  63. func ToJson(data []byte) (res []byte, err error) {
  64. prMap, err := Decode(data)
  65. if err != nil {
  66. return nil, err
  67. }
  68. return json.Marshal(prMap)
  69. }
  70. // deepSearch scans deep maps, following the key indexes listed in the sequence "path".
  71. // The last value is expected to be another map, and is returned.
  72. func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
  73. for _, k := range path {
  74. m2, ok := m[k]
  75. if !ok {
  76. // intermediate key does not exist
  77. // => create it and continue from there
  78. m3 := make(map[string]interface{})
  79. m[k] = m3
  80. m = m3
  81. continue
  82. }
  83. m3, ok := m2.(map[string]interface{})
  84. if !ok {
  85. m3 = make(map[string]interface{})
  86. m[k] = m3
  87. }
  88. // continue search from here
  89. m = m3
  90. }
  91. return m
  92. }
  93. // flattenAndMergeMap recursively flattens the given map into a new map
  94. func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
  95. if shadow != nil && prefix != "" && shadow[prefix] != nil {
  96. return shadow
  97. }
  98. var m2 map[string]interface{}
  99. if prefix != "" {
  100. prefix += delimiter
  101. }
  102. for k, val := range m {
  103. fullKey := prefix + k
  104. switch val.(type) {
  105. case map[string]interface{}:
  106. m2 = val.(map[string]interface{})
  107. case map[interface{}]interface{}:
  108. m2 = gconv.Map(val)
  109. default:
  110. // immediate value
  111. shadow[strings.ToLower(fullKey)] = val
  112. continue
  113. }
  114. // recursively merge to shadow map
  115. shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
  116. }
  117. return shadow
  118. }