2016-07-04 13 views
9

ich eine Funktion erstellen bin versucht, dieConvert Schnittstelle {} zur Karte in Golang

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

Hier struct folgende annehmen könnte könnte jede Struktur nicht nur ein spezifisches. Converting interface zu *struct oder []*struct funktioniert gut. Aber Fehler für Karte geben.

Nach reflect zeigt es, dass es map [] ist, aber einen Fehler gibt, wenn Sie versuchen, über den Bereich zu iterieren.

Hier Code

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

Gibt es eine Möglichkeit interface{} zu konvertieren abzubilden und durchlaufen oder es Elemente zu erhalten.

Antwort

10

Wenn die verwenden können Kartenwert kann ein beliebiger Typ sein, dann reflektieren, um durch die Karte iterieren:

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

playground example

Wenn ther e ist ein kleiner und bekannte Satz von Strukturtypen, dann ein type switch verwendet werden kann:

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

Schön. Ich habe die "MapKeys" zu meinem Antwortteil hinzugefügt, als du deine gepostet hast! – cnicutar

+0

Arbeiten absolut gut. Danke vielmals. – SamTech

15

Sie müssen hier nicht reflektieren. Versuchen Sie:

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

Gleiches gilt für den Rest Ihrer Funktion. Es sieht so aus, als ob Sie Reflektionen verwenden, wenn Sie besser von einer type switch bedient werden.


Alternativ, wenn Sie hier auf die Reflexion bestehen (was nicht viel Sinn macht), die Sie auch Value.MapKeys mit dem Ergebnis von Ihrem ValueOf (siehe die Antwort https://stackoverflow.com/a/38186057/714501)

+1

Hier struct jede Struktur ... wie Buch, Auther, Verlag für Typen-Schalt Art von Struktur bekannt sein könnte, dass i nicht haben. – SamTech

+0

@SamTech Wie ist der Typ der Struktur nicht "bekannt"? Sie können einfach alle Arten umschalten, die Sie behandeln möchten. – cnicutar

+0

Ich meine, woher weiß ich, ob ich Switch übergeben muss übergeben Schnittstelle zu Karte [string] * Buch oder Karte [string] * Auther? – SamTech