I'm trying to deep copy of two yaml, trying to simulate the behaviour that use helm when you pass multiple values during an installation. Because of a project restriction, I'm getting the values from strings and not directly form yamls. The problem is that the copy is not done as I expect.
I add an example:
value1 :
test:
somekey: 1
otherkey: 2
othertest:
somekey: 3
value2 :
test:
somekey: NEWVALUE1
NEWKEY: NEWVALUE2
EXPECTED OUTPUT :
test:
somekey: NEWVALUE1
otherkey: 2
NEWKEY: NEWVALUE2
othertest:
somekey: 3
RESULT :
othertest:
somekey: 3
test:
NEWKEY: NEWVALUE2
somekey: NEWVALUE1
So as you can see in the example, it is overwritting all the test
map, when I would like to merge both test
maps.
Here I add the code I'm using
package main
import (
"github.com/prometheus/common/log"
"gopkg.in/yaml.v2"
)
func main() {
value1 := "test:\n somekey: 1\n otherkey: 2\nothertest:\n somekey: 3"
value2 := "test:\n somekey: NEWVALUE1\n NEWKEY: NEWVALUE2"
values := []string{value1, value2}
result, err := getValuesFromString(values)
if err != nil {
log.Info(err)
}
//expecting: test:\n somekey: NEWVALUE1\n otherkey: 2\n NEWKEY: NEWVALUE2\nothertest:\n somekey: 3
//getting: othertest:\n somekey: 3\ntest:\n NEWKEY: NEWVALUE2\n somekey: NEWVALUE1\n
log.Info("FINAL VALUES: " + result)
}
func getValuesFromString(values []string) (string, error) {
var resultValues map[string]interface{}
var bs []byte
for _, value := range values {
log.Info(values)
var override map[string]interface{}
bs = []byte(value)
if err := yaml.Unmarshal(bs, &override); err != nil {
log.Info(err)
continue
}
//check if is nil. This will only happen for the first value
if resultValues == nil {
resultValues = override
} else {
resultValues = mergeMaps(resultValues, override)
}
}
bs, err := yaml.Marshal(resultValues)
if err != nil {
log.Info(err)
return "", err
}
return string(bs), nil
}
func mergeMaps(a, b map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(a))
for k, v := range a {
out[k] = v
}
for k, v := range b {
if v, ok := v.(map[string]interface{}); ok {
if bv, ok := out[k]; ok {
if bv, ok := bv.(map[string]interface{}); ok {
out[k] = mergeMaps(bv, v)
continue
}
}
}
out[k] = v
}
return out
}
I think the error is that I'm generating incorrectly the map from the string var resultValues map[string]interface{}
, but I didn't find the way to convert the string to a map
Use map[interface{}]interface{}
instead of map[string]interface{}
. You could have a deeper look at yaml.Unmarshal .
func mergeMaps(a, b map[interface{}]interface{}) map[interface{}]interface{} {
out := make(map[interface{}]interface{}, len(a))
for k, v := range a {
out[k] = v
}
for k, v := range b {
// If you use map[string]interface{}, ok is always false here.
// Because yaml.Unmarshal will give you map[interface{}]interface{}.
if v, ok := v.(map[interface{}]interface{}); ok {
if bv, ok := out[k]; ok {
if bv, ok := bv.(map[interface{}]interface{}); ok {
out[k] = mergeMaps(bv, v)
continue
}
}
}
out[k] = v
}
return out
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.