使用Golang yaml.v3 Node类型处理YAML嵌套节点的方法咨询
关于使用Go yaml.v3包Node类型提取子节点的解决方案
问题场景
现有如下YAML配置文件(example.yaml):
## example.yaml locationSection: firstLoc: Canada secondLoc: Korea thirdLoc: Italy numberSection: person1: 12345 person2: 98765 person3: 56789
需求是使用yaml.v3的Node类型解析该文件,生成代表整个文件的exampleNode,再从中提取locationSectionNode和numberSectionNode两个子节点,当前已有未完成的Go代码:
### this is not an exact go code but just an example... locationSectionStructure ... data, err := os.ReadFile(yamlFileFullPath) if err != nil { fmt.Printf("[I/O Error] Falied to read this file from os.ReadFile: %s", cfgFileFullPath) panic(err) } if err := yaml.Unmarshal(data, exampleNode); err != nil { fmt.Printf("[Unmarshal Error] Failed to unmarshal into a yaml Node") panic(err) } ... if err := exampleNode.Decode(locationSectionNode); err != nil { ...
简便实现方案
yaml.v3的Node类型提供了直接操作映射节点的方法,Decode是用来将Node转换为Go结构体的,不是提取子Node的正确方式,以下是完整实现:
package main import ( "fmt" "os" "gopkg.in/yaml.v3" ) func main() { yamlFileFullPath := "example.yaml" data, err := os.ReadFile(yamlFileFullPath) if err != nil { fmt.Printf("[I/O Error] Failed to read file: %s\n", yamlFileFullPath) panic(err) } // 定义代表整个YAML的根Node,必须传指针给Unmarshal var exampleNode yaml.Node if err := yaml.Unmarshal(data, &exampleNode); err != nil { fmt.Println("[Unmarshal Error] Failed to unmarshal into yaml Node") panic(err) } // 校验根节点是否为映射类型(YAML顶层是键值对结构) if exampleNode.Kind != yaml.MappingNode { fmt.Println("Root node is not a mapping structure") panic("invalid yaml format") } // 方法1:用FieldByName直接获取子节点(最简便) var locationSectionNode yaml.Node locField := exampleNode.Content[0].FieldByName("locationSection") if locField == nil { fmt.Println("[Error] locationSection not found in yaml") panic("missing key") } if err := locField.Decode(&locationSectionNode); err != nil { fmt.Println("[Error] Failed to parse locationSection node") panic(err) } fmt.Println("locationSectionNode content:") fmt.Println(locationSectionNode.String()) var numberSectionNode yaml.Node numField := exampleNode.Content[0].FieldByName("numberSection") if numField == nil { fmt.Println("[Error] numberSection not found in yaml") panic("missing key") } if err := numField.Decode(&numberSectionNode); err != nil { fmt.Println("[Error] Failed to parse numberSection node") panic(err) } fmt.Println("\nnumberSectionNode content:") fmt.Println(numberSectionNode.String()) // 方法2:手动遍历Content获取子节点(适合复杂场景) // for i := 0; i < len(exampleNode.Content); i += 2 { // keyNode := exampleNode.Content[i] // valueNode := exampleNode.Content[i+1] // if keyNode.Value == "locationSection" { // locationSectionNode = *valueNode // } else if keyNode.Value == "numberSection" { // numberSectionNode = *valueNode // } // } }
关键注意点
yaml.Unmarshal必须传入*yaml.Node指针,否则无法正确加载YAML结构- 映射类型的Node,
Content切片是按[key, value, key, value]的顺序存储的,手动遍历需要步长为2 FieldByName是映射节点的快捷方法,能直接根据键名找到对应的子节点,比手动遍历更高效
内容的提问来源于stack exchange,提问作者HONG JI
相关产品推荐
相关产品推荐

