Last active
February 16, 2021 14:29
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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