#go #yaml
#Вперед #yaml
Вопрос:
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
Я хочу извлечь значение для task
из этого yaml. Это сложно, поскольку оно может находиться по разным путям внутри файла. Это еще несколько разных путей в дополнение к приведенным выше, которые task
можно найти в файле yaml. Есть ли простой способ извлечь все значения для task
? Должен ли я конвертировать в json
?
Комментарии:
1. Что такое «значение
task
«, хотя здесь есть три разныхtask
ключа; какой из них вы считаете «значением»? Что вы пробовали до сих пор?2. Если вы знаете, как использовать
json
, вы можете сделать то же самое, но дляyaml
(вашего формата файла): pkg.go.dev/gopkg.in/yaml.v2 является хорошей отправной точкой3. @Jorropo Я мог бы довольно легко преобразовать это в JSON. Как бы вы это сделали в JSON?
4. @EliBendersky хочет получить значения для всех этих
task
ключей. Я попытался выполнить разархивирование YAML, но столкнулся с проблемами, потому что я не могу определить структуру для разархивирования, посколькуtask
ключ может встречаться во многих разных местах в файле.
Ответ №1:
go-yaml хочет, чтобы вы определили целевой тип и средство удаления для этого типа.
Если вы хотите извлечь все значения в некоторой карте с помощью ключа task
, вашим типом будет список имен задач.
Затем для этого типа вам необходимо создать функцию unmarshaler, которая принимает корневой узел файла YAML и извлекает имена задач в объект определенного вами типа.
Вот минимальный рабочий пример:
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`), amp;t); err != nil {
panic(err)
}
for _, item := range(t.items) {
fmt.Println(item)
}
}
Вывод:
task1
task2
task3
Имейте в виду, что это решение, как правило, неверно, поскольку узел YAML может содержать циклы (из-за привязок / псевдонимов YAML), что приведет к переполнению стека, поскольку код не проверяет циклы.