英文:
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.Sif
是 nil
) 插入值。你可以在赋值之前使用以下代码创建一个空的 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论