Golang 遍历接口图

Golang iterate over map of interfaces

我正在尝试迭代 golang 中的接口映射,它具有以下结构,我可以使用 for 循环迭代到单个级别但无法深入获取接口的值。

Yaml

steps:
  execute:
  - mvn : 1.9.3
    goals: 'clean install'
    concurrent: false
  - mvn : 1.9.3
    goals: 'dependency-check:check'
    concurrent: false

Go

// reading a yaml file 
// trying to use "gopkg.in/yaml.v2" package

data, err := ioutil.ReadFile(fileName)
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
    for k, v := range m {
       // Trying to explore the data here
        fmt.Println("k:", k, "v:", v)

    }

的输出
fmt.Printf("--- m:\n%v\n\n", m)

如下所示

map[steps:map[execute:[map[concurrent:false goals:clean install mvn:1.9.3] map[concurrent:false goals:dependency-check:check mvn:1.9.3]]]]

我的尝试

for k, v := range m {
    fmt.Println("k:", k, "v:", v)

}

我不知道你所说的“m 的值”是什么意思,因为它看起来不像我在 Go 中看到的任何格式所以我将讨论几种情况:当接口可能是类型你知道它们是什么 vs 什么时候接口可以是任何东西但你不确定。

如果您知道地图中只有几种类型,您可以执行 type switch and handle each type individually. This would give you the opportunity to reference subfields and print them as well. If you always print the same info for the same type of objects, you could look into adding a String() string function to your structs which will make them implement the Stringer interface 然后您可以打印该对象,即使它被装箱,您的 String() 函数也会被调用作为接口。

如果您正在使用一个正在填充地图的库,或者地图中类型的多样性太大以至于类型切换不合理,那么您要么需要一个通用的解决方案,例如像 spew or a custom solution written with reflection.

这样的图书馆

假设你有一个 map[interface{}]interface{} 和 []interface{} 的树,使用下面的代码遍历树:

func walk(v interface{}) {
    switch v := v.(type) {
    case []interface{}:
        for i, v := range v {
            fmt.Println("index:", i)
            walk(v)
        }
    case map[interface{}]interface{}:
        for k, v := range v {
            fmt.Println("key:", k)
            walk(v)
        }
    default:
        fmt.Println(v)
    }
}

此代码向下递归结构并使用 type switches 查找切片和映射。

这样使用:

data, err := ioutil.ReadFile(fileName)
var m map[interface{}]interface{}
err = yaml.Unmarshal([]byte(data), &m)
walk(m)

Run it on the playground

要访问 Execute 对象并对其进行迭代,您必须执行很多类型断言,例如@vooker 给出的答案。

实际上不建议使用 map[interface{}]interface{} 进行解组处理。它应该作为结构解组为具体类型。

从YAML结构来看,可以翻译成这样的结构类型:


type data struct {
    Steps struct {
        Execute []execute `yaml:"execute"`
    } `yaml:"steps"`
}

type execute struct {
    MVN        string `yaml:"mvn"`
    Goals      string `yaml:"goals"`
    Concurrent bool   `yaml:"concurrent"`
}

并且从结构来看,更容易从 YAML 访问未编组的数据。例如,如果你想迭代“执行”对象,你可以这样做:

    var result data
    yaml.Unmarshal([]byte(str), &result)
    fmt.Println(result) 

    // iterate over the slice of object
    for _, e := range result.Steps.Execute {
        fmt.Println()
        fmt.Println("mvn:", e.MVN)
        fmt.Println("goals:", e.Goals)
        fmt.Println("concurrent:", e.Concurrent)
    }

playground

上试用