Go: How can I "unpack" a struct? - reflection

I have a struct:
type mystruct struct {
Foo string
Bar int
}
I'd like to create SQL insert statements from the struct which have the following form:
m := mystruct{ "Hello" , 1 }
query := "INSERT INTO mytbl ( foo, bar ) VALUES ( ?,? )"
res,err := db.Exec(query, m.Foo, m.Bar)
Now my question is: how can I make the last line dynamically from the struct (or m) itself? I am able to get the struct names using reflect, but I don't know how to create the []interface{} slice for the db.Exec() call. This is what I have tried: (http://play.golang.org/p/GR1Bb61NFH)
package main
import (
"fmt"
"reflect"
)
type mystruct struct {
Foo string
Bar int
}
func main() {
m := mystruct{"Foo", 1}
fmt.Println(readNames(m))
x := unpackStruct(m)
fmt.Printf("%#v\n", x)
}
func unpackStruct(a interface{}) []interface{} {
// "convert" a to m t
// doesn't work, from 'laws of reflection'
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f.Type(), f.Interface())
}
// this is in principle what I want:
m := mystruct{"Hello", 2}
var ret []interface{}
ret = make([]interface{}, s.NumField())
ret[0] = m.Foo
ret[1] = m.Bar
return ret
}
// works fine:
func readNames(a interface{}) []string {
s := reflect.TypeOf(a)
lenStruct := s.NumField()
ret := make([]string, lenStruct)
for i := 0; i < lenStruct; i++ {
ret[i] = s.Field(i).Name
}
return ret
}

If getting the values of the fields is your issue, this code snippet should help:
s := reflect.ValueOf(a)
ret := make([]interface{}, s.NumField())
for i := 0; i < s.NumField(); i++ {
ret[i] = s.Field(i).Interface()
}

If creating the []interface{} value is your problem, using reflect's slice creation mechanisms should work nicely:
slc := reflect.MakeSlice(InterfaceType, len, cap) // See the link below for creating InterfaceType
slc.Index(0).Set(TargetValue)
return slc.Interface()
(Here's the above-mentioned link).
Modifying the above code to loop over the values in the struct instead of just the 0th index shouldn't be too bad.

Related

Reflection on slice of pointers

I am trying to reflect a slice of pointers on a struct stored in an interface{}
I think I am doing ok until it's time to introspect the content on the pointed struct.
See the below example
package main
import (
"fmt"
"reflect"
)
type teststruct struct {
prop1 string
prop2 string
}
func main() {
test := teststruct{"test", "12"}
var container interface{}
var testcontainer []*teststruct
testcontainer = append(testcontainer, &test)
container = testcontainer
rcontainer := reflect.ValueOf(container)
fmt.Println(rcontainer.Kind())
rtest := rcontainer.Index(0).Elem()
fmt.Println(rtest)
rteststruct := reflect.ValueOf(rtest)
fmt.Println(rteststruct.Kind())
typeOfT := rteststruct.Type()
for i := 0; i < rteststruct.NumField(); i++ {
f := rteststruct.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.String())
}
}
Which results
slice
{test 12}
struct
0: typ *reflect.rtype = <*reflect.rtype Value>
1: ptr unsafe.Pointer = <unsafe.Pointer Value>
2: flag reflect.flag = <reflect.flag Value>
I am definitely missing something here, someone would be able to explain me what ?
rtest := rcontainer.Index(0).Elem() is already the value, so when you do this: rteststruct := reflect.ValueOf(rtest), you are actually getting a reflect.Value which is of course a struct.
Replace the end of your code with this:
typeOfT := rtest.Type()
for i := 0; i < rtest.NumField(); i++ {
f := rtest.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.String())
}
Playground

Modifying a struct slice within a struct in Go

In the following example, a person has a slice of friendships, and I try to initialize a friendship as a pointer to another person object, but for some reason it fails, and the result is that nobody has any friendships. Am I not using a pointer somewhere where I should be?
package main
import (
"fmt"
"math/rand"
)
type friendship struct {
friend *person
}
type person struct {
name int
friendship []friendship
}
func createPerson(id int) person {
return person{id, make([]friendship, 0)}
}
func (p *person) addFriends(possibleFriends []*person, numFriends int) {
var friend *person
for i := 0; i < numFriends; i++ {
friend = possibleFriends[rand.Intn(len(possibleFriends))]
p.friendship = append(p.friendship, friendship{friend})
}
}
func main() {
numPeople := 20
people := make([]person, numPeople)
possibleFriends := make([]*person, numPeople)
for i := 0; i < numPeople; i++ {
people[i] = createPerson(i)
possibleFriends[i] = &(people[i])
}
for _, p := range people {
p.addFriends(possibleFriends, 2)
}
fmt.Println(people)
}
use
for i := 0; i < numPeople; i++ {
people[i].addFriends(possibleFriends, 2)
}
or
for i, _ := range people {
people[i].addFriends(possibleFriends, 2)
}
instead of
for _, p := range people {
p.addFriends(possibleFriends, 2)
}
this is because p is a copy of people[i], addFriends has no effect on slice people

How to convert interface{} to map

I am trying to create a function that could accept following
*struct
[]*struct
map[string]*struct
Here struct could be any struct not just a specific one.
Converting interface to *struct or []*struct is working fine.
But giving error for map.
After reflect it shows it is map[] but giving error when try to iterate over range.
Here is 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)
}
Is there any way to convert interface{} to map and iterate or get it's elements.
If the map value can be any type, then use reflect to iterate through the map:
if v.Kind() == reflect.Map {
for _, key := range v.MapKeys() {
strct := v.MapIndex(key)
fmt.Println(key.Interface(), strct.Interface())
}
}
playground example
If there's a small and known set of struct types, then a type switch can be used:
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
}
}
You don't need reflect here. Try:
v, ok := in.(map[string]*Book)
if !ok {
// Can't assert, handle error.
}
for _, s := range v {
fmt.Printf("Value: %v\n", s)
}
Same goes for the rest of your function. It looks like you're using reflection when you would be better served by a type switch.
Alternatively, if you insist on using reflection here (which doesn't make a lot of sense) you can also use Value.MapKeys with the result from your ValueOf (see the answer https://stackoverflow.com/a/38186057/714501)
This may help:
b := []byte(`{"keyw":"value"}`)
var f interface{}
json.Unmarshal(b, &f)
myMap := f.(map[string]interface{})
fmt.Println(myMap)
Another way to convert an interface{} into a map with the package reflect is with MapRange.
I quote:
MapRange returns a range iterator for a map. It panics if v's Kind is
not Map.
Call Next to advance the iterator, and Key/Value to access each entry.
Next returns false when the iterator is exhausted. MapRange follows
the same iteration semantics as a range statement.
Example:
iter := reflect.ValueOf(m).MapRange()
for iter.Next() {
key := iter.Key().Interface()
value := iter.Value().Interface()
...
}

More easy way of using Interfaces to map YAML dynamically?

I am trying to parse a yaml file dynamically (Therefore no struct).
package main
import (
"fmt"
"gopkg.in/yaml.v2"
"log"
)
func main() {
var out = `
a: First!
f: Second
b:
c:
f: Third
`
m := make(map[interface{}]interface{})
err := yaml.Unmarshal([]byte(out), &m)
if err != nil {
log.Fatal(err)
}
fmt.Println(m["b"].(map[interface{}]interface{})["c"].(map[interface{}]interface{})["f"])
}
Everytime I have to access a subkey, I am forced to convert map variable in question to (map[interface{}]interface{}). This is causing bit of a hassle for me as I have to iterate through the map.
Is there any easier method for parsing YAML file in Go?
Another approach is to flatten the yaml data structure into a key,value map in which keys and values are strings. Then if you need the actual type (5 being an int) you can do the conversion yourself. Example:
"a" = "First!"
"f" = "Second"
"b.c.f" = "Third"
"b.c.g.size" = "2"
"b.c.g.0 = "zero"
"b.c.g.1 = "one"
In Go:
func main() {
any := map[string]interface{}{}
err := yaml.Unmarshal([]byte(out), &any)
if err != nil {
log.Fatal(err)
}
flatmap := map[string]string{}
for k, v := range any {
flatten(k, v, flatmap)
}
for k, v := range flatmap {
fmt.Println(k, "=", v)
}
}
func flatten(prefix string, value interface{}, flatmap map[string]string) {
submap, ok := value.(map[interface{}]interface{})
if ok {
for k, v := range submap {
flatten(prefix+"."+k.(string), v, flatmap)
}
return
}
stringlist, ok := value.([]interface{})
if ok {
flatten(fmt.Sprintf("%s.size", prefix), len(stringlist), flatmap)
for i, v := range stringlist {
flatten(fmt.Sprintf("%s.%d", prefix, i), v, flatmap)
}
return
}
flatmap[prefix] = fmt.Sprintf("%v", value)
}

How to cast reflect.Value to its type?

How to cast reflect.Value to its type?
type Cat struct {
Age int
}
cat := reflect.ValueOf(obj)
fmt.Println(cat.Type()) // Cat
fmt.Println(Cat(cat).Age) // doesn't compile
fmt.Println((cat.(Cat)).Age) // same
Thanks!
concreteCat,_ := reflect.ValueOf(cat).Interface().(Cat)
see http://golang.org/doc/articles/laws_of_reflection.html
fox example
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)
Ok, I found it
reflect.Value has a function Interface() that converts it to interface{}
This func auto-converts types as needed. It loads a config file values into a simple struct based on struct name and fields:
import (
"fmt"
toml "github.com/pelletier/go-toml"
"log"
"os"
"reflect"
)
func LoadConfig(configFileName string, configStruct interface{}) {
defer func() {
if r := recover(); r != nil {
fmt.Println("LoadConfig.Recovered: ", r)
}
}()
conf, err := toml.LoadFile(configFileName)
if err == nil {
v := reflect.ValueOf(configStruct)
typeOfS := v.Elem().Type()
sectionName := getTypeName(configStruct)
for i := 0; i < v.Elem().NumField(); i++ {
if v.Elem().Field(i).CanInterface() {
kName := conf.Get(sectionName + "." + typeOfS.Field(i).Name)
kValue := reflect.ValueOf(kName)
if (kValue.IsValid()) {
v.Elem().Field(i).Set(kValue.Convert(typeOfS.Field(i).Type))
}
}
}
} else {
fmt.Println("LoadConfig.Error: " + err.Error())
}
}
Seems the only way would be to do a switch statement similar to (code below) (also, something like the commented line would've-been nice though doesn't work (:()):
func valuesFromStruct (rawV interface{}) []interface{} {
v := reflect.ValueOf(rawV)
out := make([]interface{}, 0)
for i := 0; i < v.NumField(); i += 1 {
field := v.Field(i)
fieldType := field.Type()
// out = append(out, field.Interface().(reflect.PtrTo(fieldType)))
switch (fieldType.Name()) {
case "int64":
out = append(out, field.Interface().(int64))
break`enter code here`
case "float64":
out = append(out, field.Interface().(float64))
break
case "string":
out = append(out, field.Interface().(string))
break
// And all your other types (here) ...
default:
out = append(out, field.Interface())
break
}
}
return out
}
Cheers!

Resources