【问题标题】:How to convert interface{} to map如何将 interface{} 转换为地图
【发布时间】:2016-11-06 06:09:39
【问题描述】:

我正在尝试创建一个可以接受关注的函数

*struct
[]*struct
map[string]*struct

这里的结构可以是任何结构,而不仅仅是一个特定的结构。 将接口转换为*struct[]*struct 工作正常。 但是给地图报错。

反映后显示它是 map[] 但尝试迭代范围时出错。

这里是代码

package main

import (
    "fmt"
    "reflect"
)

type Book struct {
    ID     int
    Title  string
    Year   int
}

func process(in interface{}, isSlice bool, isMap bool) {
    v := reflect.ValueOf(in)

    if isSlice {
        for i := 0; i < v.Len(); i++ {
            strct := v.Index(i).Interface()
            //... proccess struct
        }
        return
    }

    if isMap {
        fmt.Printf("Type: %v\n", v)     // map[]
        for _, s := range v {           // Error: cannot range over v (type reflect.Value)
            fmt.Printf("Value: %v\n", s.Interface())
        }
    }    
}

func main() {
    b := Book{}
    b.Title = "Learn Go Language"
    b.Year = 2014
    m := make(map[string]*Book)
    m["1"] = &b

    process(m, false, true)
}

有没有办法将interface{} 转换为映射和迭代或获取它的元素。

【问题讨论】:

    标签: dictionary reflection go interface


    【解决方案1】:

    这可能会有所帮助:

    b := []byte(`{"keyw":"value"}`)
    
    var f interface{}
    json.Unmarshal(b, &f)
    
    myMap := f.(map[string]interface{})
    
    fmt.Println(myMap)
    

    【讨论】:

      【解决方案2】:

      使用反射包将interface{} 转换为map 的另一种方法是使用MapRange

      我引用:

      MapRange 返回地图的范围迭代器。如果 v 的 Kind 是 不是地图。

      调用 Next 来推进迭代器,调用 Key/Value 来访问每个条目。 当迭代器耗尽时,Next 返回 false。地图范围如下 与范围语句相同的迭代语义。

      例子:

      iter := reflect.ValueOf(m).MapRange()
      for iter.Next() {
          key := iter.Key().Interface()
          value := iter.Value().Interface()
          ...
      }
      

      【讨论】:

        【解决方案3】:

        如果地图值可以是任何类型,则使用反射来遍历地图:

        if v.Kind() == reflect.Map {
            for _, key := range v.MapKeys() {
                strct := v.MapIndex(key)
                fmt.Println(key.Interface(), strct.Interface())
            }
        }
        

        playground example

        如果有一组已知的小型结构类型,则可以使用type switch

        func process(in interface{}) {
          switch v := in.(type) {
          case map[string]*Book:
             for s, b := range v {
                 // b has type *Book
                 fmt.Printf("%s: book=%v\n" s, b)
             }
          case map[string]*Author:
             for s, a := range v {
                 // a has type *Author
                 fmt.Printf("%s: author=%v\n" s, a)
             }
           case []*Book:
             for i, b := range v {
                 fmt.Printf("%d: book=%v\n" i, b)
             }
           case []*Author:
             for i, a := range v {
                 fmt.Printf("%d: author=%v\n" i, a)
             }
           case *Book:
             fmt.Ptintf("book=%v\n", v)
           case *Author:
             fmt.Printf("author=%v\n", v)
           default:
             // handle unknown type
           }
        }
        

        【讨论】:

        • 不错。当您发布您的答案时,我将MapKeys 添加到我的答案部分!
        • 工作得很好。非常感谢。
        【解决方案4】:

        你不需要在这里反思。试试:

        v, ok := in.(map[string]*Book)
        if !ok {
            // Can't assert, handle error.
        }
        for _, s := range v {
            fmt.Printf("Value: %v\n", s)
        }
        

        您的其他功能也是如此。当type switch 为您提供更好的服务时,您似乎正在使用反射。


        或者,如果您坚持在这里使用反射(这没有多大意义),您也可以使用 Value.MapKeys 与您的 ValueOf 的结果(请参阅答案 https://stackoverflow.com/a/38186057/714501

        【讨论】:

        • 这里的结构可以是任何结构,如 Book、Auther、Publisher ... 用于类型切换的结构类型必须是已知的,我没有。
        • @SamTech 结构的类型如何不“已知”?您可以切换您想要处理的所有类型。
        • 我的意思是,我怎么知道我是否必须输入 switch 传递的接口到 map[string]*Book 或 map[string]*Auther ?
        • @SamTech 两者都打开。
        猜你喜欢
        • 2020-07-17
        • 1970-01-01
        • 2021-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-10
        • 2015-01-14
        • 2018-12-25
        相关资源
        最近更新 更多