Golang中如何读取YAML文件内的重复print字段?
问题解答
1. 默认解析行为:重复键会被覆盖
常用的Go YAML解析库(比如gopkg.in/yaml.v3)默认处理重复顶级键时,会用最后出现的值覆盖之前的。比如你提供的示例YAML,直接用普通结构体解析的话,最终只会得到3,前面的1和2会丢失。
2. 如何获取所有重复的print字段
要保留所有重复键的对应值,有两种可行的实现方式:
方法一:用yaml.Node手动遍历解析
先把整个YAML内容解析成yaml.Node,再遍历所有顶级节点,收集所有print键对应的值:
package main import ( "fmt" "os" "gopkg.in/yaml.v3" ) func main() { data, err := os.ReadFile("test.yaml") if err != nil { panic(err) } var root yaml.Node if err := yaml.Unmarshal(data, &root); err != nil { panic(err) } var printValues []int // 遍历顶级映射的键值对(映射节点的Content是[key1, value1, key2, value2...]的结构) for i := 0; i < len(root.Content); i += 2 { keyNode := &root.Content[i] if keyNode.Value == "print" { var val int if err := root.Content[i+1].Decode(&val); err != nil { panic(err) } printValues = append(printValues, val) } } fmt.Println("所有print字段的值:", printValues) // 输出 [1 2 3] }
方法二:自定义类型实现yaml.Unmarshaler接口
定义一个自定义类型,在解析逻辑里主动收集所有print键的值:
package main import ( "fmt" "os" "gopkg.in/yaml.v3" ) type PrintList struct { Prints []int } func (p *PrintList) UnmarshalYAML(value *yaml.Node) error { if value.Kind != yaml.MappingNode { return fmt.Errorf("expected mapping node") } for i := 0; i < len(value.Content); i += 2 { keyNode := value.Content[i] if keyNode.Value == "print" { var val int if err := value.Content[i+1].Decode(&val); err != nil { return err } p.Prints = append(p.Prints, val) } } return nil } func main() { data, err := os.ReadFile("test.yaml") if err != nil { panic(err) } var pl PrintList if err := yaml.Unmarshal(data, &pl); err != nil { panic(err) } fmt.Println("所有print字段的值:", pl.Prints) // 输出 [1 2 3] }
3. 推荐的替代方案(规范YAML结构)
重复键属于YAML的非规范写法,不同解析器的处理逻辑可能存在差异,长期来看容易引发兼容性问题。更稳妥的做法是修改YAML结构,把重复的print字段改成数组形式:
prints: - 1 - 2 - 3
对应的Go结构体定义非常简洁:
type Print struct { Prints []int `yaml:"prints"` }
解析代码也更简单直观:
package main import ( "fmt" "os" "gopkg.in/yaml.v3" ) type Print struct { Prints []int `yaml:"prints"` } func main() { data, err := os.ReadFile("test.yaml") if err != nil { panic(err) } var p Print if err := yaml.Unmarshal(data, &p); err != nil { panic(err) } fmt.Println("所有print字段的值:", p.Prints) // 输出 [1 2 3] }
内容的提问来源于stack exchange,提问作者DFG
相关产品推荐
相关产品推荐

