mirror of
https://gitee.com/johng/gf
synced 2026-07-06 21:45:34 +08:00
change decode/encode lib for properties to magiconair
This commit is contained in:
@ -8,67 +8,136 @@
|
||||
package gproperties
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/magiconair/properties"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// Decode converts properties format to map.
|
||||
func Decode(data []byte) (res map[string]interface{}, err error) {
|
||||
res = make(map[string]interface{})
|
||||
var (
|
||||
bytesReader = bytes.NewReader(data)
|
||||
bufioReader = bufio.NewReader(bytesReader)
|
||||
)
|
||||
vp := viper.New()
|
||||
vp.SetConfigType("properties")
|
||||
if err = vp.ReadConfig(bufioReader); err != nil {
|
||||
err = gerror.Wrapf(err, `viper ReadConfog failed`)
|
||||
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
|
||||
}
|
||||
res = vp.AllSettings()
|
||||
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) {
|
||||
var (
|
||||
//w = new(bytes.Buffer)
|
||||
vp = viper.New()
|
||||
tmpFileName = fmt.Sprintf("vp_tmp_config%s", gtime.Now().Format("YmdHis"))
|
||||
)
|
||||
vp.SetConfigName(tmpFileName)
|
||||
vp.SetConfigType("properties")
|
||||
vp.AddConfigPath(".")
|
||||
vp.MergeConfigMap(data)
|
||||
if err = vp.SafeWriteConfig(); err != nil {
|
||||
err = gerror.Wrapf(err, `viper WriteConfog failed`)
|
||||
return nil, err
|
||||
}
|
||||
tmpFileNameP := tmpFileName + ".properties"
|
||||
res, err = ioutil.ReadFile(tmpFileNameP)
|
||||
defer os.Remove(tmpFileNameP)
|
||||
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, cast.ToString(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, `Read viper tmp file failed`)
|
||||
err = gerror.Wrapf(err, `Properties Write buf failed.`)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// ToJson convert .properties format to JSON.
|
||||
func ToJson(data []byte) (res []byte, err error) {
|
||||
iniMap, err := Decode(data)
|
||||
prMap, err := Decode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(iniMap)
|
||||
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 {
|
||||
// intermediate key is a value
|
||||
// => replace with a new map
|
||||
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 {
|
||||
// prefix is shadowed => nothing more to flatten
|
||||
return shadow
|
||||
}
|
||||
if shadow == nil {
|
||||
shadow = make(map[string]interface{})
|
||||
}
|
||||
|
||||
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 = cast.ToStringMap(val)
|
||||
default:
|
||||
// immediate value
|
||||
shadow[strings.ToLower(fullKey)] = val
|
||||
continue
|
||||
}
|
||||
// recursively merge to shadow map
|
||||
shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
|
||||
}
|
||||
return shadow
|
||||
}
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
package gproperties_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
@ -19,44 +19,74 @@ import (
|
||||
)
|
||||
|
||||
var pStr string = `
|
||||
# template
|
||||
data = "/home/www/templates/"
|
||||
# MySQL
|
||||
sql.disk.0 = 127.0.0.1:6379,0
|
||||
sql.cache.0 = 127.0.0.1:6379,1=
|
||||
sql.cache.1=0
|
||||
sql.disk.a = 10
|
||||
# 模板引擎目录
|
||||
viewpath = "/home/www/templates/"
|
||||
# MySQL数据库配置
|
||||
redis.disk = "127.0.0.1:6379,0"
|
||||
redis.cache = "127.0.0.1:6379,1"
|
||||
`
|
||||
var errorTests = []struct {
|
||||
input, msg string
|
||||
}{
|
||||
// unicode literals
|
||||
{"key\\u1 = value", "invalid unicode literal"},
|
||||
{"key\\u12 = value", "invalid unicode literal"},
|
||||
{"key\\u123 = value", "invalid unicode literal"},
|
||||
{"key\\u123g = value", "invalid unicode literal"},
|
||||
{"key\\u123", "invalid unicode literal"},
|
||||
|
||||
// circular references
|
||||
{"key=${key}", `circular reference in:\nkey=\$\{key\}`},
|
||||
{"key1=${key2}\nkey2=${key1}", `circular reference in:\n(key1=\$\{key2\}\nkey2=\$\{key1\}|key2=\$\{key1\}\nkey1=\$\{key2\})`},
|
||||
|
||||
// malformed expressions
|
||||
{"key=${ke", "malformed expression"},
|
||||
{"key=valu${ke", "malformed expression"},
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
|
||||
decodeStr, err := gproperties.Decode(([]byte)(pStr))
|
||||
m := make(map[string]interface{})
|
||||
m["properties"] = pStr
|
||||
res, err := gproperties.Encode(m)
|
||||
if err != nil {
|
||||
t.Errorf("encode failed. %v", err)
|
||||
return
|
||||
}
|
||||
decodeMap, err := gproperties.Decode(res)
|
||||
if err != nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%v\n", decodeStr)
|
||||
v, _ := json.Marshal(decodeStr)
|
||||
fmt.Printf("%v\n", string(v))
|
||||
t.Assert(decodeMap["properties"], pStr)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for _, v := range errorTests {
|
||||
_, err := gproperties.Decode(([]byte)(v.input))
|
||||
if err == nil {
|
||||
t.Errorf("encode should be failed. %v", err)
|
||||
return
|
||||
}
|
||||
t.AssertIN(`Lib magiconair load Properties data failed.`, strings.Split(err.Error(), ":"))
|
||||
}
|
||||
})
|
||||
}
|
||||
func TestEncode(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
|
||||
encStr, err := gproperties.Encode(map[string]interface{}{
|
||||
"sql": g.Map{
|
||||
"userName": "admin",
|
||||
"password": "123456",
|
||||
},
|
||||
"user": "admin",
|
||||
"no": 123,
|
||||
})
|
||||
m := make(map[string]interface{})
|
||||
m["properties"] = pStr
|
||||
res, err := gproperties.Encode(m)
|
||||
if err != nil {
|
||||
t.Errorf("encode failed. %v", err)
|
||||
return
|
||||
}
|
||||
decodeMap, err := gproperties.Decode(res)
|
||||
if err != nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%v\n", string(encStr))
|
||||
t.Assert(decodeMap["properties"], pStr)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user