解析YAML文件的Go语言方法

34

我正在尝试使用Go解析一个yaml文件。不幸的是,我无法弄清楚该怎么做。我有一个yaml文件,内容如下:

---
firewall_network_rules:
  rule1:
    src:       blablabla-host
    dst:       blabla-hostname
...

我有这段Go代码,但它不起作用:

package main

import (
    "fmt"
    "io/ioutil"
    "path/filepath"

    "gopkg.in/yaml.v2"
)

type Config struct {
    Firewall_network_rules map[string][]string
}

func main() {
    filename, _ := filepath.Abs("./fruits.yml")
    yamlFile, err := ioutil.ReadFile(filename)

    if err != nil {
        panic(err)
    }

    var config Config

    err = yaml.Unmarshal(yamlFile, &config)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Value: %#v\n", config.Firewall_network_rules)
}

当我运行这个程序时,会出现一个错误。我认为这是因为我还没有为源(src)和目标(dst)键/值创建结构体。顺便说一下,当我将其更改为列表时,它可以正常工作。

所以上述代码解析了这个内容:

---
firewall_network_rules:
  rule1:
    - value1
    - value2
...
4个回答

48

如果您正在使用Google Cloud或特别是Kubernetes,并且想要解析类似于此的service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: myName
  namespace: default
  labels:
    router.deis.io/routable: "true"
  annotations:
    router.deis.io/domains: ""
spec:
  type: NodePort
  selector:
    app: myName
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443

提供一个真实世界的例子,以便您了解如何编写嵌套。

type Service struct {
    APIVersion string `yaml:"apiVersion"`
    Kind       string `yaml:"kind"`
    Metadata   struct {
        Name      string `yaml:"name"`
        Namespace string `yaml:"namespace"`
        Labels    struct {
            RouterDeisIoRoutable string `yaml:"router.deis.io/routable"`
        } `yaml:"labels"`
        Annotations struct {
            RouterDeisIoDomains string `yaml:"router.deis.io/domains"`
        } `yaml:"annotations"`
    } `yaml:"metadata"`
    Spec struct {
        Type     string `yaml:"type"`
        Selector struct {
            App string `yaml:"app"`
        } `yaml:"selector"`
        Ports []struct {
            Name       string `yaml:"name"`
            Port       int    `yaml:"port"`
            TargetPort int    `yaml:"targetPort"`
            NodePort   int    `yaml:"nodePort,omitempty"`
        } `yaml:"ports"`
    } `yaml:"spec"`
}

有一个非常方便的服务叫做yaml-to-go https://zhwt.github.io/yaml-to-go/,它可以将YAML转换为go结构体。只需要将你的YAML输入到该服务中,你就能得到自动生成的结构体。

同样也有一个JSON的等效工具:https://mholt.github.io/json-to-go/

最后,正如之前的发帖者所说的,使用unmarshal也是一种方法:

var service Service

err = yaml.Unmarshal(yourFile, &service)
if err != nil {
    panic(err)
}

fmt.Print(service.Metadata.Name)

12

嗯,我想我已经自己解决了。下面的代码可以正常工作。有任何建议/改进吗?

package main

import (
    "fmt"
    "io/ioutil"
    "path/filepath"

    "gopkg.in/yaml.v2"
)

type Config struct {
    Firewall_network_rules map[string]Options
}

type Options struct {
    Src string
    Dst string
}

func main() {
    filename, _ := filepath.Abs("./fruits.yml")
    yamlFile, err := ioutil.ReadFile(filename)

    if err != nil {
        panic(err)
    }

    var config Config

    err = yaml.Unmarshal(yamlFile, &config)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Value: %#v\n", config.Firewall_network_rules)
}

4
尝试使用更习惯用语的"FirewallNetworkRules",并添加一个结构标签来捕获YAML格式,例如yaml:"firewall_network_rules"。有关YAML库中结构标记用法的文档,请参见此处:http://godoc.org/gopkg.in/yaml.v2#Marshal - elithrar
感谢您的建议,它确实澄清了我的代码。 - Rogier Lommers

10

如果您不关心规则名称,为什么不将您的YAML文件组织成以下形式?

---
firewall_network_rules:
  - 
    name:      rule1
    src:       blablabla-host
    dst:       blabla-hostname
  - 
    name:      rule2
    src:       bla-host
    dst:       bla-hostname

因此,代码将像这样,它是干净和可扩展的:

type Rule struct {
    Name  string  `yaml:"name"`
    Src   string  `yaml:"src"`
    Dst   string  `yaml:"dst"`
}

type Config struct {
   FirewallNetworkRules []Rule  `yaml:"firewall_network_rules"`
}

2
如果您的YAML文件很简单(只有一层嵌套),就像下面这样
mongo:
    DB: database
    COL: collection
log:
    error: log/error/error.log
api:
    key: jhgwewbcjwefwjfg

在这里,你可以使用接口来代替声明结构体。
main(){
  config := Config()
  mongoConfig := config["mongo"]

  mongo.MongoDial(
    String(
        Get(mongoConfig, "DB")
    ), 
    String(
        Get(mongoConfig, "COL")
    )
  )
}

func Config() map[string]interface{} {
    filename, _ := filepath.Abs("configs/config.yaml")
    yamlFile, err := ioutil.ReadFile(filename)

    if err != nil {
        panic(err)
    }

    var config map[string]interface{}

    err = yaml.Unmarshal(yamlFile, &config)
    if err != nil {
        panic(err)
    }

    return config
}
func Get(this interface{}, key string) interface{}  {
    return this.(map[interface{}]interface{})[key]
}
func String(payload interface{}) string  {
    var load string
    if pay, oh := payload.(string); oh {
        load = pay
    }else{
        load = ""
    }
    return load
}

如果嵌套层数较深,建议使用struct来处理复杂的嵌套关系。对于一级嵌套,这种方法可以很好地运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接