【问题标题】:Unmarshalling YAML array of multiple types解组多种类型的 YAML 数组
【发布时间】:2021-02-27 04:34:36
【问题描述】:

我对 Go 还有些陌生,需要一些帮助。我有一个 YAML 的 sn-p,我想将其解组为我可以使用的东西。

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"`
}

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

【问题讨论】:

  • 所有带有- 的连续行都被视为数组项,因此脚本属性是一个数组。正确的表示是Script []string。数组中的第一项是可执行文件,其余是参数。
  • @gp.So 我试过了。这得到了参数,但没有得到可执行文件。我无法弄清楚如何同时获得两者。我似乎只能得到一个或另一个。
  • 知道了。第一项不是字符串。使用ScriptConfig interface{} 将允许解码,但是您需要检查类型和类型转换。

标签: go yaml unmarshalling


【解决方案1】:

脚本属性是数组,其中第一项是结构,第二项是字符串。 选项 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 看起来也更具可读性。如果没有,那么上述选项应该会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    相关资源
    最近更新 更多