Go yaml how to add new entry to yaml file

huangapple go评论89阅读模式
英文:

Go yaml how to add new entry to yaml file

问题

我需要将一个结构值config.Sif["snk_prod"]添加到一个yaml文件中(我需要在运行时填充它),我尝试了以下方法,但在填充结构时出现错误,有什么想法吗?

package main

import "gopkg.in/yaml.v3"

const document = `
spec:
  mec:
    customConfig:
      sif:
        prom_exporter:
          type: prometheus_exporter
        snk_dev:   
          type: sk_hc_logs
          inputs:
            - tesslt
          ep: ${NT}
          dken: ${SN}
`

type YamlObject map[string]interface{}

type CustomConfig struct {
	Sif map[string]interface{} `yaml:"sif,omitempty"`
}

type ecfg struct {
	Type   string   `yaml:"type,omitempty"`
	Inputs []string `yaml:"inputs,omitempty"`
	Ep     string   `yaml:"ep,omitempty"`
	Dken   string   `yaml:"dken,omitempty"`
}

func main() {
	t := CustomConfig{}
	config := &t
	// -------I need to add it as struct values as I got the values on runtime dynamically  
	config.Sif["snk_prod"] = ecfg{
		Type:   "sk_hc_ls",
		Inputs: []string{"tesslt"},
		Ep:     "${NT}",
	}
	yamlBytes, err := yaml.Marshal(t)

	doc := make(YamlObject)
	if err := yaml.Unmarshal([]byte(document), &doc); err != nil {
		panic(err)
	}
	addon := make(YamlObject)
	if err := yaml.Unmarshal(yamlBytes, &addon); err != nil {
		panic(err)
	}
	node := findChild(doc, "spec", "mec", "customConfig", "sif")
	if node == nil {
		panic("Must not happen")
	}

	for key, val := range addon {
		(*node)[key] = val
	}
	outDoc, err := yaml.Marshal(doc)
	if err != nil {
		panic(err)
	}
	println(string(outDoc))
}

func findChild(obj YamlObject, path ...string) *YamlObject {
	if len(path) == 0 {
		return &obj
	}
	key := path[0]
	child, ok := obj[key]
	if !ok {
		return nil
	}
	obj, ok = child.(YamlObject)
	if !ok {
		return nil
	}
	return findChild(obj, path[1:]...)
}

在搜索后,我找到了这个答案https://stackoverflow.com/a/74089724/6340176,它与我的问题非常相似,不同之处在于我需要将其作为结构值添加。

最后,我需要在sif下添加新的条目。

最后的输出应该如下所示

spec:
  mec:
    customConfig:
      sif:
        prom_exporter:
          type: prometheus_exporter
        snk_dev:   
          type: sk_hc_logs
          inputs:
            - tesslt
          ep: ${NT}
          dken: ${SN}
        snk_prod:   
          type: sk_hc_ls
          inputs:
            - tesslt
          ep: ${NT}
英文:

I need to add entry to a yaml file from struct value config.Sif["snk_prod"] (I need to fill it on runtime) and try the following , I tried the following but I got an error when filling the struct, any idea?

package main
import "gopkg.in/yaml.v3"
const document = `
spec:
mec:
customConfig:
sif:
prom_exporter:
type: prometheus_exporter
snk_dev:   
type: sk_hc_logs
inputs:
- tesslt
ep: ${NT}
dken: ${SN}
`
type YamlObject map[string]any
type CustomConfig struct {
Sif map[string]interface{} `yaml:"sif,omitempty"`
}
type ecfg struct {
Type   string   `yaml:"type,omitempty"`
Inputs []string `yaml:"inputs,omitempty"`
Ep     string   `yaml:"ep,omitempty"`
Dken   string   `yaml:"dken,omitempty"`
}
func main() {
t := CustomConfig{}
config := &t
// -------I need to add it as struct values as I got the values on runtime dynamically  
config.Sif["snk_prod"] = ecfg{
Type:   "sk_hc_ls",
Inputs: []string{"tesslt"},
Ep:     "${NT}",
}
yamlBytes, err := yaml.Marshal(t)
doc := make(YamlObject)
if err := yaml.Unmarshal([]byte(document), &doc); err != nil {
panic(err)
}
addon := make(YamlObject)
if err := yaml.Unmarshal(yamlBytes, &addon); err != nil {
panic(err)
}
node := findChild(doc, "spec", "mec", "customConfig", "sif")
if node == nil {
panic("Must not happen")
}
for key, val := range addon {
(*node)[key] = val
}
outDoc, err := yaml.Marshal(doc)
if err != nil {
panic(err)
}
println(string(outDoc))
}
func findChild(obj YamlObject, path ...string) *YamlObject {
if len(path) == 0 {
return &obj
}
key := path[0]
child, ok := obj[key]
if !ok {
return nil
}
obj, ok = child.(YamlObject)
if !ok {
return nil
}
return findChild(obj, path[1:]...)
}

https://go.dev/play/p/6CHsOJPXqpw

After searching I found this answer https://stackoverflow.com/a/74089724/6340176
which is quite similar to mine, the change is the that I need to add it as struct value

at the end I need to add new entry under sif

At the end The output should be like following

spec:
mec:
customConfig:
sif:
prom_exporter:
type: prometheus_exporter
snk_dev:   
type: sk_hc_logs
inputs:
- tesslt
ep: ${NT}
dken: ${SN}
snk_prod:   
type: sk_hc_ls
inputs:
- tesslt
ep: ${NT}

答案1

得分: 2

将创建yamlBytes的部分替换为以下内容:

t := make(map[string]interface{})
t["snk_prod"] = ecfg{
    Type:   "sk_hc_ls",
    Inputs: []string{"tesslt"},
    Ep:     "${NT}",
}
yamlBytes, err := yaml.Marshal(t)

然后你将得到预期的结果。

顺便说一下:触发 panic 是因为你试图向一个未初始化的 map (config.Sifnil) 插入值。你可以在赋值之前使用以下代码创建一个空的 map:

config.Sif = make(map[string]interface{})

但是这样会在代码中多出一个不需要的 sif 节点,例如:

...
sif:
    prom_exporter:
        type: prometheus_exporter
    sif:
        snk_prod:
...

因此,动态添加的 yaml 片段应该按照开头所示的方式生成。

英文:

Replace the creation of the yamlBytes as follows:

	t := make(map[string]interface{})
t["snk_prod"] = ecfg{
Type:   "sk_hc_ls",
Inputs: []string{"tesslt"},
Ep:     "${NT}",
}
yamlBytes, err := yaml.Marshal(t)

Then you will get the expected result.

BTW: The panic is triggered because you are trying to insert a value into an uninitialized map (config.Sif is nil). You could simply create an empty map using, for example, the following line of code before assigning values:

config.Sif = make(map[string]interface{})

but there would be an additional unwanted sif node in the code, e.g. something like this:

    ...
sif:
prom_exporter:
type: prometheus_exporter
sif:
snk_prod:
...

Therefore, the yaml snippet to be added dynamically should be generated as shown at the beginning.

huangapple
  • 本文由 发表于 2022年10月23日 03:12:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/74166433.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定