Преобразование интерфейса{} в карту в Golang

Я пытаюсь создать функцию, которая может принимать следующие

*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: %vn", v)     // map[]
        for _, s := range v {           // Error: cannot range over v (type reflect.Value)
            fmt.Printf("Value: %vn", 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{} для отображения и итерации или получения элементов.

2 ответов


Если значение карты может быть любого типа, то используйте reflect для итерации по карте:

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

детская площадка пример

Если есть небольшой и известный набор типов структур, то тип переключателя можно использовать в:

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
   }
}

вам не нужно размышлять здесь. Попробуйте:

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

то же самое касается остальной части вашей функции. Похоже, вы используете отражение, когда вам будет лучше служить тип переключателя.


кроме того, если вы настаиваете на использовании отражения здесь (что не имеет большого смысла), вы также можете использовать Value.MapKeys С результатом от вашего ValueOf (см. ответ https://stackoverflow.com/a/38186057/714501)