Skip to content

Instantly share code, notes, and snippets.

@robertgro
Last active February 16, 2021 14:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robertgro/6fd0f1c9e7c7345985b5bfe32fed2ec0 to your computer and use it in GitHub Desktop.
Save robertgro/6fd0f1c9e7c7345985b5bfe32fed2ec0 to your computer and use it in GitHub Desktop.
Golang reflection to use in conjunction with sql package's rows.Scan() for any (nested) struct passed in as interface{}, please refer to the disclaimer within this file
package main
import (
"fmt"
"reflect"
)
// to be used in association with the golang sql package, rows.Scan()
// pass in any struct object/object slice you like and get appropriate results using reflection
// restriction: the sql query result's columns length must match the struct object fields length (len(rows.Columns(), fix your query)
// feel free to use, share, modify, expand, w/e. no license agreement
// annotations left, primarily for me to learn more about golang, reflection and data types as such
// this example pulls 'sidebar' items into a slice for furhter usage (json marshalling)
// doesn't claim to be idiomatic or best practice. use at your own risk!
type (
navItem struct {
ID int
Name string
}
navItemList struct {
NvL []navItem
}
dbWorker struct {
queryObj interface{}
queryObjL interface{}
}
)
func (dW *dbWorker) AddNavItem() {
rSPtr := ptr(reflect.ValueOf(dW.queryObjL))
rVPtr := ptr(reflect.ValueOf(dW.queryObj))
/*
println(rSPtr.String(), rSPtr.Elem().String(), rSPtr.Elem().Elem().String(), rSPtr.Elem().Elem().CanSet())
<**main.navItemList Value> <*main.navItemList Value> <main.navItemList Value> true
*/
ev := rSPtr.Elem().Elem().Field(0) // may be a subject to change due dynamic field allocation demand
/*
println(ev.String(), ev.Type().String(), ev.CanSet())
<[]main.navItem Value> []main.navItem true
*/
ev.Set(reflect.Append(ev, rVPtr.Elem().Elem()))
}
func (dW *dbWorker) changeValue(rv reflect.Value, cols []interface{}) {
// rows.Scan(): due to interface{} being a pointer to type of v, dereference properly
for i, v := range cols {
switch v.(type) {
case int: // *int
rv.Field(i).SetInt(int64(v.(int))) // rv.Field(i).SetInt(int64(*(v.(*int))))
case string: // *string
rv.Field(i).SetString(v.(string)) // rv.Field(i).SetString(*(v.(*string)))
case bool: // *bool
rv.Field(i).SetBool(v.(bool)) // rv.Field(i).SetBool(*(v.(*bool)))
default:
fmt.Printf("Type assert not defined. Type is %T", v)
}
}
}
func (dW *dbWorker) rowsNextIteration(newint int, newstr string) {
rv := ptr(reflect.ValueOf(dW.queryObj))
/*
println(rv.String(), rv.Elem().String(), rv.Elem().Elem().String(), rv.Elem().Elem().CanSet())
<**main.navItem Value> <*main.navItem Value> <main.navItem Value> true
*/
em := rv.Elem().Elem()
num := em.NumField()
cols := make([]interface{}, num)
for i := 0; i < num; i++ {
field := em.Field(i)
cols[i] = field.Interface()
/*
switch cols[i].(type) {
case int:
println("ID-A", cols[i].(int))
cols[i] = 3
field.SetInt(int64(cols[i].(int)))
case string:
println("NAME-A", cols[i].(string))
cols[i] = "str2"
field.SetString(cols[i].(string))
default:
fmt.Printf("Type assert not defined. Type is %T", v)
}
*/
}
/*
println("ID-A", cols[0].(int))
println("NAME-A", cols[1].(string))
*/
cols[0] = newint
cols[1] = newstr
dW.changeValue(em, cols)
dW.AddNavItem()
}
func main() {
ni := &navItem{}
ni.ID = 2
ni.Name = "str1"
nis := &navItemList{}
myWorker := &dbWorker{}
println("NvL-A", len(nis.NvL))
/*
nis.NvL = append(nis.NvL, *ni)
println("NvL-B", len(nis.NvL))
*/
myWorker.queryObjL = nis
myWorker.queryObj = ni
myWorker.rowsNextIteration(3, "str2")
myWorker.rowsNextIteration(4, "str3")
myWorker.rowsNextIteration(5, "str4")
/*
myWorker.AddNavItem()
println("ID-B", ni.ID)
println("NAME-B", ni.Name)
*/
println("NvL-B", len(nis.NvL))
printSlice(*nis)
}
func printSlice(niL navItemList) {
for k, v := range niL.NvL {
println("NavItems", "key", k, "id", v.ID, "name", v.Name)
}
}
// credits to https://github.com/a8m/reflect-examples
// ptr wraps the given value with pointer: V => *V, *V => **V, etc.
func ptr(v reflect.Value) reflect.Value {
pt := reflect.PtrTo(v.Type()) // create a *T type.
pv := reflect.New(pt.Elem()) // create a reflect.Value of type *T.
pv.Elem().Set(v) // sets pv to point to underlying value of v.
return pv
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment