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