Reputation: 61
jobs:
- name: test
public: true
plan:
- try:
task: task1
file: test1.yaml
on_success:
in_parallel:
steps:
- task: task2
file: test2.yaml
- task: task3
file: task3.yaml
I want to extract the value for task
from this yaml. It's tricky since it can be at different paths within the file. These are a few more different paths in addition to the above that task
can be found in the yaml file. Is there an easy way to extract all values for task
? Should I convert to json
?
Upvotes: 2
Views: 2169
Reputation: 39668
go-yaml wants you to define a target type and an unmarshaler for that type.
If you want to extract all values in some map with the key task
, your type would be a list of task names.
For that type, you then need to create an unmarshaler function which takes the YAML file's root node and extracts the task names into the object of the type you defined.
Here's a minimal working example:
package main
import (
"errors"
"fmt"
"gopkg.in/yaml.v3"
)
type Tasks struct {
items []string
}
// descend implements recursive descent into YAML mapping and sequence structures
func (t *Tasks) descend(node *yaml.Node) error {
switch node.Kind {
case yaml.SequenceNode:
for _, item := range(node.Content) {
t.descend(item)
}
case yaml.MappingNode:
for i := 0; i < len(node.Content); i += 2 {
key := node.Content[i]
value := node.Content[i+1]
if key.Kind != yaml.ScalarNode ||
key.Value != "task" {
t.descend(value)
continue
}
if value.Kind != yaml.ScalarNode {
return errors.New("encountered non-scalar task")
}
t.items = append(t.items, value.Value)
}
}
return nil
}
// UnmarshalYAML is the unmarshaler that will be called by the YAML processor.
func (t *Tasks) UnmarshalYAML(value *yaml.Node) error {
t.items = nil
return t.descend(value)
}
func main() {
var t Tasks
// I fixed some whitespace issues in your YAML input
if err := yaml.Unmarshal([]byte(`jobs:
- name: test
public: true
plan:
- try:
task: task1
file: test1.yaml
on_success:
in_parallel:
steps:
- task: task2
file: test2.yaml
- task: task3
file: task3.yaml`), &t); err != nil {
panic(err)
}
for _, item := range(t.items) {
fmt.Println(item)
}
}
Output:
task1
task2
task3
Mind that this solution is not generally correct as a YAML node may contain cycles (due to YAML anchors/aliases) which would lead to a stack overflow because the code does not check against cycles.
Upvotes: 1