change decode/encode lib for properties to magiconair

This commit is contained in:
unknown
2022-05-12 00:23:46 +08:00
parent 59023f9f09
commit 060f67c2c8
4 changed files with 167 additions and 762 deletions

View File

@ -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
}

View File

@ -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)
})
}