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