Golang解析YAML时遇到问题怎么解决

Golang解析YAML时遇到问题怎么解决 我有以下YAML配置。这里有一些代码仓库(repo1, repo2)和发行版(wheezy-test, centos7等)。

repos:

  repo1:
    centos6-test:
      type:  rpm
      path:  /repo/centos/repo1/centos6-test/x86_64
    centos7:
      type: rpm
      sign: 'Secret123'
      path:  /repo/centos/repo1/centos7/x86_64

  repo2:
    wheezy-test:
      origin: EvilCorp
      label:  EvilCorp
      path:   /repo/debian/repo2/wheezy-test
      blacklist:
      - -dbg
    centos7:
      type: rpm
      sign: 'Secret123'
      path: /repo/debian/repo2/7/1.1/x86_64
      blacklist:
      - -debuginfo

我推测要解析这个配置,我需要应用类似下面的结构:

type RepoConf struct {
    Repos Repos `yaml:"repos"`
}
type Repos struct {
    Repo1 Repo1 `yaml:"repo1"`
    Repo2 Repo2 `yaml:"repo2"`
}
type Repo1 struct {
    Centos6Test Centos6Test `yaml:"centos6-test"`
    Centos7     Centos7     `yaml:"centos7"`
}
type Repo2 struct {
    WheezyTest WheezyTest `yaml:"wheezy-test"`
    Centos7    Centos7    `yaml:"centos7"`
}
type Centos6Test struct {
    Type string `yaml:"type"`
    Path string `yaml:"path"`
}
type Centos7 struct {
    Type string `yaml:"type"`
    Sign string `yaml:"sign"`
    Path string `yaml:"path"`
}
..........

但是我有太多“repoN”和发行版(wheezy-test, wheezy, centos6/7/8等等)的变体。

是否可以在不为每个仓库和发行版创建不同结构的情况下解析这个YAML配置?或者我必须重构我的YAML配置,使其变成类似这样:

repos:
    - repo1:
      - name: wheezy
        sign: ABC
        origin: EvilCorp
        label:  EvilCorp
        path: /somepath/bla1
        blacklist:
          - bla
      - name: wheezy-test
        sign: ABC
        origin: EvilCorp
        label:  EvilCorp
        path: /somepath/bla2
        blacklist:
          - lib

    - repo2:
      - name: centos7
        sign: '72B865FD'
        path: /somepath/bla3
        blacklist:
          - lib

非常感谢任何建议。


更多关于Golang解析YAML时遇到问题怎么解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

谢谢,Jeff。我已经使用了您建议的那种结构。

更多关于Golang解析YAML时遇到问题怎么解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


能否先解析到 map[string]interface{},然后再将映射转换为你期望的内部结构?

可以使用 map[string]interface{} 或嵌套的 map 结构来动态解析YAML,避免为每个仓库和发行版创建单独的结构体。以下是示例代码:

package main

import (
    "fmt"
    "gopkg.in/yaml.v3"
)

func main() {
    yamlData := `
repos:
  repo1:
    centos6-test:
      type: rpm
      path: /repo/centos/repo1/centos6-test/x86_64
    centos7:
      type: rpm
      sign: 'Secret123'
      path: /repo/centos/repo1/centos7/x86_64
  repo2:
    wheezy-test:
      origin: EvilCorp
      label: EvilCorp
      path: /repo/debian/repo2/wheezy-test
      blacklist:
        - -dbg
    centos7:
      type: rpm
      sign: 'Secret123'
      path: /repo/debian/repo2/7/1.1/x86_64
      blacklist:
        - -debuginfo
`

    var config map[string]interface{}
    err := yaml.Unmarshal([]byte(yamlData), &config)
    if err != nil {
        panic(err)
    }

    repos := config["repos"].(map[string]interface{})
    for repoName, repoData := range repos {
        fmt.Printf("仓库: %s\n", repoName)
        distributions := repoData.(map[string]interface{})
        for distName, distData := range distributions {
            fmt.Printf("  发行版: %s\n", distName)
            distMap := distData.(map[string]interface{})
            for key, value := range distMap {
                fmt.Printf("    %s: %v\n", key, value)
            }
        }
    }
}

如果需要类型安全,可以定义部分结构体结合 map 使用:

type RepoConfig struct {
    Repos map[string]map[string]Distribution `yaml:"repos"`
}

type Distribution struct {
    Type      string   `yaml:"type,omitempty"`
    Sign      string   `yaml:"sign,omitempty"`
    Path      string   `yaml:"path"`
    Origin    string   `yaml:"origin,omitempty"`
    Label     string   `yaml:"label,omitempty"`
    Blacklist []string `yaml:"blacklist,omitempty"`
}

func main() {
    yamlData := `...` // 同上YAML数据
    
    var config RepoConfig
    err := yaml.Unmarshal([]byte(yamlData), &config)
    if err != nil {
        panic(err)
    }

    for repoName, distributions := range config.Repos {
        fmt.Printf("仓库: %s\n", repoName)
        for distName, dist := range distributions {
            fmt.Printf("  发行版: %s\n", distName)
            fmt.Printf("    路径: %s\n", dist.Path)
            if dist.Sign != "" {
                fmt.Printf("    签名: %s\n", dist.Sign)
            }
            if len(dist.Blacklist) > 0 {
                fmt.Printf("    黑名单: %v\n", dist.Blacklist)
            }
        }
    }
}

这种方法可以处理任意数量的仓库和发行版,无需为每个组合创建单独的结构体。

回到顶部