Go中读取YAML文件时如何保留类别与任务的顺序?
问题描述
我需要读取如下格式的YAML文件:
Category Name: Task 1: Name: Meeting PM: 1 TC: 0 STC: 1 Optional: false
文件支持多个类别,每个类别下可包含多个任务。我原本用以下方式读取:
定义结构体:
type Task struct { Name string PM string TC string STC string Optional bool } type Category struct { Name string Tasks []Task }
解析函数:
func parseFile() map[string]map[string]map[string]string { file, err := ioutil.ReadFile("config.yaml") if err != nil { log.Fatal(err) } data := make(map[string]map[string]map[string]string) err1 := yaml.Unmarshal(file, &data) if err1 != nil { log.Fatal(err1) } return data }
之后遍历返回的map生成Category列表,虽然能正常运行,但map是无序的,无法保留YAML里类别和任务的原有顺序。
我了解到可以用gopkg.in/yaml.v2的yaml.MapSlice来保留顺序,但不知道怎么处理得到的MapSlice:能遍历获取类别,但MapItem.Value是interface类型,里面存的是任务列表,不知道怎么把它转换成[]Task类型。
解决方案
要保留YAML的原有顺序,直接用yaml.MapSlice逐层解析即可,核心是对每个层级的interface{}类型做类型断言,转换成对应的yaml.MapSlice再处理。
步骤1:用yaml.MapSlice接收顶层数据
顶层是类别列表,用yaml.MapSlice接收,每个元素的Key是类别名称,Value是该类别下的任务列表(同样是yaml.MapSlice类型)。
步骤2:逐层遍历并转换类型
遍历顶层MapSlice拿到每个类别,再遍历类别对应的MapSlice拿到每个任务项,把任务项的Value断言成map[string]interface{},然后映射到Task结构体上。
完整代码示例:
import ( "io/ioutil" "log" "gopkg.in/yaml.v2" ) type Task struct { Name string PM string TC string STC string Optional bool } type Category struct { Name string Tasks []Task } func parseFile() []Category { file, err := ioutil.ReadFile("config.yaml") if err != nil { log.Fatal(err) } var topLevel yaml.MapSlice err = yaml.Unmarshal(file, &topLevel) if err != nil { log.Fatal(err) } var categories []Category for _, catItem := range topLevel { // 获取类别名称 catName, ok := catItem.Key.(string) if !ok { log.Fatal("invalid category name type") } // 断言类别下的任务列表为MapSlice taskSlice, ok := catItem.Value.(yaml.MapSlice) if !ok { log.Fatal("invalid tasks type for category:", catName) } var tasks []Task for _, taskItem := range taskSlice { // 断言任务内容为map[string]interface{} taskData, ok := taskItem.Value.(map[string]interface{}) if !ok { log.Fatal("invalid task data type for task:", taskItem.Key) continue } // 映射到Task结构体 task := Task{ Name: taskData["Name"].(string), PM: taskData["PM"].(string), TC: taskData["TC"].(string), STC: taskData["STC"].(string), Optional: taskData["Optional"].(bool), } tasks = append(tasks, task) } categories = append(categories, Category{ Name: catName, Tasks: tasks, }) } return categories }
注意事项
- 类型断言时最好加上错误判断,避免YAML格式异常导致程序崩溃;
- 如果YAML中数值类型(比如PM、TC)是数字而非字符串,需要调整
Task结构体对应字段的类型,或者在断言时做类型转换(比如strconv.Itoa(taskData["PM"].(int))); - 该方式严格保留YAML文件中类别和任务的原有顺序。
内容的提问来源于stack exchange,提问作者BlueCanary
相关产品推荐
相关产品推荐

