github.com/gogf/gf/v2@v2.7.4/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/gogf/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/gogf/gf/v2/errors/gerror"
    18  	"github.com/gogf/gf/v2/internal/json"
    19  	"github.com/gogf/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  }