首页 > 解决方案 > 解组多种类型的 YAML 数组

问题描述

我对 Go 还是有点陌生​​,可以使用一些帮助。我有一个 YAML 片段,我想将其解组为我可以使用的东西。

YAML:

script:
  - executable: python
  - script.py

这些是我目前拥有的结构。我能得到Executable,但我不能得到参数列表。

type Config struct {
    Script []ScriptConfig `yaml:"script"`
}

type ScriptConfig struct {
    Executable string   `yaml:"executable"`
    Arguments  []string `yaml:",flow"`
}

有人能帮我吗?如何获取参数列表?

标签: goyamlunmarshalling

解决方案


脚本属性是数组,其中第一项是结构,第二项是字符串。选项 1:制作 ScriptConfig 接口,但在解码后您需要进行类型转换并进一步解码第一个 ScriptConfig 条目。

type ScriptConfig interface{}

type Config struct {
    Script []ScriptConfig `yaml:"script"`
}

选项 2:使 ScriptConfig 成为另一个具有 arg 和可执行两个属性的结构,并使用自定义封送拆收器。但是,您将拥有 nil 需要注意的属性之一,并且从您的实体模型中它也不是很干净。

type ScriptConfig struct {
    Executable string `yaml:"executable"`
    Arg string
}

func (s *ScriptConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
    var exec struct {
        Executable string `yaml:"executable"`
    }
    if err := unmarshal(&exec); err != nil {
        // will be error for 2nd row onwards
        fmt.Println(err)
    }
    var arg string
    if err := unmarshal(&arg); err != nil {
        // will be error for 1st row
        fmt.Println(err)
    }
    s.Executable = exec.Executable
    s.Arg = arg
    return nil
}

选项 3:使用yaml.Node和临时结构,并使用自定义封送器将转换为您想要的实际结构。这有点复杂,但你会得到干净的实体模型。

type tmpConfig struct {
    // Script []ScriptConfig `yaml:"script"`
    Script []yaml.Node `yaml:"script"`
}
type tmpExecutable struct {
    Executable string `yaml:"executable"`
}

type ScriptConfig struct {
    Executable string
    Args       []string
}
type Config struct {
    Script ScriptConfig `yaml:"script"`
}

func (c *Config) UnmarshalYAML(v *yaml.Node) error {
    var tmp tmpConfig
    if err := v.Decode(&tmp); err != nil {
        return err
    }
    l := len(tmp.Script)
    if l < 1 {
        return nil
    }

    var tmpexec tmpExecutable
    if err := tmp.Script[0].Decode(&tmpexec); err != nil {
        return err
    }

    //copy the executable property from the decoded temp struct
    c.Script.Executable = tmpexec.Executable

    c.Script.Args = make([]string, l-1)
    //copy the args skipping first one
    for i := 1; i < len(tmp.Script); i++ {
        c.Script.Args[i-1] = tmp.Script[i].Value
    }

    fmt.Println(c)
    return nil
}

一般注意事项:检查 yaml 本身是否可以使用单独的可执行文件和 args 进行结构化,这样 yaml 看起来也更具可读性。如果没有,那么上述选项应该会有所帮助。


推荐阅读