【问题标题】:Golang iterate over map of interfacesGolang 遍历接口映射
【发布时间】:2020-07-21 00:43:32
【问题描述】:

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

Yaml

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

// 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)

}

【问题讨论】:

    标签: go go-interface go-map


    【解决方案1】:

    假设您有一棵由 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

    【讨论】:

      【解决方案2】:

      要访问 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上试试

      【讨论】:

        【解决方案3】:

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

        如果您知道地图中只有几种类型,您可以执行type switch 并单独处理每种类型。这将使您有机会引用子字段并打印它们。如果您总是为相同类型的对象打印相同的信息,您可以考虑在您的结构中添加一个String() string 函数,这将使它们实现Stringer interface,然后您可以打印该对象并且您的 String() 函数将即使它被装箱为接口也会被调用。

        如果您正在使用一个正在填充地图的库,或者地图中的类型种类太多以至于类型切换不合理,那么您可能需要一个通用解决方案,例如库比如spew 或使用reflection 编写的自定义解决方案。

        【讨论】:

        • 道歉,我应该说得更清楚。我已经编辑了这个问题。谢谢。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-14
        • 2018-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-15
        相关资源
        最近更新 更多