Reflection in Go allows to manipulate objects and are most useful when dealing with structs.
I always wished I could range over a struct. But “range” only supports builtin types such as string, list, and map. Reflect makes it easy to do so.
I have a package that contains a function, I use frequently to do exactly this. It takes in a struct and copies over the elements to a map so we can range over it. https://github.com/mariadesouza/structutil
Here is a small example:
package main import ( "fmt" "github.com/mariadesouza/structutil" ) type testStruct struct { Name string Email []string Occupation string } func main() { test := testStruct{"Ethan", []string{"emdesouza@gmail.com"}, "engineer"} m := structutil.StructToMap(&test) for key, val := range *m { fmt.Println(key, ":", val) } }
How it works?
Main concepts used:
type
Every variable in GO has a static type including elements of a struct.
interface{}
In go an interface{} represents an empty set of methods that satisfies all variable types
reflect
Go has a package called reflect that allows us to inspect the type and value stored in any data type.
I used reflect on a struct and turn it into a map of names to values. The call to ValueOf returns a Value representing the run-time data. This helps us to loop through the runtime elements
structmap := make(map[string]interface{}) // map to hold key - element elements := reflect.ValueOf(myStruct).Elem() typeofT := elements.Type() // type of Element for i := 0; i <elements.NumField(); i++ { f := elements.Field(i) elementName := typeofT.Field(i).Name //key name of element valueOfElement := f.Interface() //returns current value as an interface{} structmap[elementName] = valueOfElement }
For this to work, the struct elements have to be exported i.e. start with a capital or you will get a panic error as below.
panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
https://play.golang.org/p/Kyn-k4rAFFX
Access private struct fields using reflection
We can read private struct variables in a package using reflection. However, private field values cannot be changed.
E.g.
package main import ( "fmt" "reflect" "container/list" ) func main() { cl := list.New() cl.PushFront(2) fieldvaluelen := reflect.ValueOf(cl).Elem().FieldByName("len") fmt.Println(fieldvaluelen.Int()) } Output: 1