Find the real caller in method call - reflection

I have a struct like this:
type Parent struct {
example string
}
func (p *Parent) GetMyAttr() {
typ := reflect.TypeOf(p).Elem()
for i := 0; i < typ.NumField(); i++ {
p := typ.Field(i)
if !p.Anonymous {
fmt.Println(p.Name, ":", p.Type)
}
}
}
And if I have another struct like this:
type Child struct {
Parent
another string
}
call GetTypeOfMe() in child like this
ch := Child{Parent{"example"},"another"}
ch.GetMyAttr()
is always return example : string .
Is this possible to get Child struct in Parent struct with reflection?
Full code is here http://play.golang.org/p/ej4Xh_V2J1

It doesn't work like this, reflect.TypeOf(p).Elem() will always return Parent you have 2 options, either implement GetMyAttr for each child or use a generic function like :
func GetAttr(i interface{}) {
typ := reflect.Indirect(reflect.ValueOf(i)).Type()
fmt.Println(typ)
for i := 0; i < typ.NumField(); i++ {
p := typ.Field(i)
fmt.Println(p.Name, ":", p.Type)
}
}

No, since you operate on a *Parent, not a *Child.
This would "work" (play.golang)
func (p *Child) GetMyAttr() {...
would output:
Parent : main.Parent
another : string
A Parent doesn't know about the struct which might embed it, so the reflection operates only on Parent struct.
You see a similar issue with "Go Reflection with Embedding", which uses interface{} to get to all the struct passed in parameter.

Related

Go slice pointer in variable changes behaviour during append

I'm a bit confused as to why this happens when attempting to remove an element from a slice in Go.
As a simplified example, I have the following data structures:
type Person struct {
Name string
}
type People []Person
I want to add a method to remove a person with a certain name from the list of people with people.Remove("Sam"):
func (p *People) Remove(name string) {
for i, person := range *p {
if person.Name == name {
// Doesn't work
*p = append(*p[:i], *p[i+1:]...)
}
}
}
I thought this would work, but referencing the slice elements in this manner returns a compile error: cannot slice p (type *People).
So, fair enough. But when I set *p to another variable (a), it works exactly as expected:
func (p *People) Remove(name string) {
for i, person := range *p {
if person.Name == name {
// Does work
a := *p
*p = append(a[:i], a[i+1:]...)
}
}
}
Why do I need to set the slice to a difference variable before this method works? Shouldn't the behaviour be exactly the same?
I'm probably misunderstanding something fundamental here so if anyone can explain why this is required I'd love to know. Thank you!
The slice expressions in *p = append(*p[:i], *p[i+1:]...) take precedence over the * operator. You need to enclose those in parentheses.
func (p *People) Remove(name string) {
for i, person := range *p {
if person.Name == name {
// Doesn't work
*p = append((*p)[:i], (*p)[i+1:]...)
}
}
}
https://play.golang.org/p/cbT65mCzA4h

Recursion function on a struct in Golang

I have the following code to organise *Widget structs into a hierarchy. Parent() returns the widget with an ID of the caller's parentID. The hierarchy can be up to 4 or 5 levels deep.
type Widget struct {
ID int64
ParentID int64
}
func (w *Widget) Parent() *Widget {
// Returns the widget with an ID of w.ParentID
}
What I want to achieve is a function which aggregates the parent, as well as the parent's parent etc. to the top of the hierarchy, and returns a slice of all of the parent IDs. As I don't know the depth of the hierarchy, I think I need some sort of programmatic recursion to get the parent of each parent, which would do something like the following:
func (w *Widget) AllParents() []*Widget {
var parentWidgets []*Widget
x := w.Parent()
parentWidgets = append(parentWidgets, x)
y := x.Parent()
parentWidgets = append(parentWidgets, y)
...
return parentWidgets
}
Is there a more idiomatic way of achieving this?
You just need to step higher and higher in the hierarchy until you reach the root. Assuming Widget.Parent() returns nil if the Widget is the "root" (meaning it has no parent):
Iterative solution
Here is the solution with a simple for loop:
func (w *Widget) AllParents() []*Widget {
var ws []*Widget
for parent := w.Parent(); parent != nil; parent = parent.Parent() {
ws = append(ws, parent)
}
return ws
}
This method will return nil if called on the "root" (zero value for all slice types). In other cases, it will return the "path" from the direct parent leading to the "root".
Here's a little test program. It creates a root widget with ID = 0, a child with ID = 1 and a "grandchild" with ID = 2, and prints AllParents() called on each. To implement Widget.Parent(), I used a simple "widget registry" map.
Result as expected:
Parents of 0:
Parents of 1: 0
Parents of 2: 1 0
Try it on the Go Playground.
var wreg = map[int64]*Widget{}
func (w *Widget) Parent() *Widget {
// Returns the widget with an ID of w.ParentID
return wreg[w.ParentID]
}
func main() {
w := &Widget{0, -1}
wreg[w.ID] = w
w2 := &Widget{1, 0}
wreg[w2.ID] = w2
w3 := &Widget{2, 1}
wreg[w3.ID] = w3
printParents(w)
printParents(w2)
printParents(w3)
}
func printParents(w *Widget) {
fmt.Printf("Parents of %d:", w.ID)
for _, w := range w.AllParents() {
fmt.Print(" ", w.ID)
}
fmt.Println()
}
Recursive solution
Of course it can be solved using recursion too:
func (w *Widget) AllParents() []*Widget {
if parent := w.Parent(); parent == nil {
return nil
} else {
return append(parent.AllParents(), parent)
}
}
This solution returns the "path" from the root leading to the direct parent.
Running the above test program with this implementation, output is:
Parents of 0:
Parents of 1: 0
Parents of 2: 0 1
Try this on the Go Playground.

Reflect: setting a field of a pointer

I'm trying to do something like this:
Define structs with tags named env:
type Env struct {
Port string `env:"PORT"`
}
Call some function which will get the environment variable names using os.Getenv and put set it in the struct.
Right now, I have this:
package main
import (
"fmt"
"os"
"reflect"
)
func ParseEnv(t interface{}, v interface{}) {
it := reflect.TypeOf(t)
for i := 0; i < it.NumField(); i++ {
field := it.Field(i)
value := os.Getenv(field.Tag.Get("env"))
if value == "" {
continue
}
reflect.ValueOf(v).Elem().FieldByName(field.Name).SetString(value)
}
}
type Env struct {
Port string `env:"PORT"`
DatabaseURL string `env:"DATABASE_URL"`
}
func main() {
os.Setenv("PORT", "8080")
os.Setenv("DATABASE_URL", "postgres://user:pass#host:5432/my-db")
env := Env{}
ParseEnv(env, &env)
fmt.Println(env)
}
http://play.golang.org/p/b8uPPVo4aV
But, as you can see, I have to pass both the reference and the pointer to my function.
While this works, it is very ugly (at least I think it is).
If I try to pass the pointer only, I can't get the type right (because it will be an *interface{}) and, if I pass only the reference, I can't set the values using reflect (even if I could, it would not work).
Is there a sane way of doing this?
Below is a "saner" way of doing what you want. You will notice that, instead of passing in two copies of the struct, we only need a pointer to the struct.
func ParseEnv(val interface{}) {
ptrRef := reflect.ValueOf(val)
if ptrRef.Kind() != reflect.Ptr {
panic("pointer to struct expected")
}
ref := ptrRef.Elem()
if ref.Kind() != reflect.Struct {
panic("pointer to struct expected")
}
refType := ref.Type()
for i := 0; i < refType.NumField(); i++ {
field := refType.Field(i)
value := os.Getenv(field.Tag.Get("env"))
if value == "" {
continue
}
ref.Field(i).SetString(value)
}
}
The above function should be invoked in the following way:
ParseEnv(&env)
Example: https://play.golang.org/p/_BwWz2oUql

Changing a slice by passing its pointer

I have a slice that I want to change (for example i want to remove the first element) using a function. I thought to use a pointer, but I still can't index it. What am I doing wrong?
Playground link:
func change(list *[]int) {
fmt.Println(*list)
*list = *list[1:] //This line screws everything up
}
var list = []int{1, 2, 3}
func main() {
change(&list)
}
You need to use (*list).
func change(list *[]int) {
*list = (*list)[1:]
}
or a different approach that's usually more go idomatic:
func change(list []int) []int {
return list[1:]
}
playground

function for converting a struct to map in Golang

I want to convert a struct to map in Golang. It would also be nice if I could use the JSON tags as keys in the created map (otherwise defaulting to field name).
Edit Dec 14, 2020
Since structs repo was archived, you can use mapstructure instead.
Edit TL;DR version, Jun 15, 2015
If you want the fast solution for converting a structure to map, see the accepted answer, upvote it and use that package.
Happy coding! :)
Original Post
So far I have this function, I am using the reflect package but I don't understand well how to use the package, please bear with me.
func ConvertToMap(model interface{}) bson.M {
ret := bson.M{}
modelReflect := reflect.ValueOf(model)
if modelReflect.Kind() == reflect.Ptr {
modelReflect = modelReflect.Elem()
}
modelRefType := modelReflect.Type()
fieldsCount := modelReflect.NumField()
var fieldData interface{}
for i := 0; i < fieldsCount; i++ {
field := modelReflect.Field(i)
switch field.Kind() {
case reflect.Struct:
fallthrough
case reflect.Ptr:
fieldData = ConvertToMap(field.Interface())
default:
fieldData = field.Interface()
}
ret[modelRefType.Field(i).Name] = fieldData
}
return ret
}
Also I looked at JSON package source code, because it should contain my needed implementation (or parts of it) but don't understand too much.
I also had need for something like this. I was using an internal package which was converting a struct to a map. I decided to open source it with other struct based high level functions. Have a look:
https://github.com/fatih/structs
It has support for:
Convert struct to a map
Extract the fields of a struct to a []string
Extract the values of a struct to a []values
Check if a struct is initialized or not
Check if a passed interface is a struct or a pointer to struct
You can see some examples here: http://godoc.org/github.com/fatih/structs#pkg-examples
For example converting a struct to a map is a simple:
type Server struct {
Name string
ID int32
Enabled bool
}
s := &Server{
Name: "gopher",
ID: 123456,
Enabled: true,
}
// => {"Name":"gopher", "ID":123456, "Enabled":true}
m := structs.Map(s)
The structs package has support for anonymous (embedded) fields and nested structs. The package provides to filter certain fields via field tags.
From struct to map[string]interface{}
package main
import (
"fmt"
"encoding/json"
)
type MyData struct {
One int
Two string
Three int
}
func main() {
in := &MyData{One: 1, Two: "second"}
var inInterface map[string]interface{}
inrec, _ := json.Marshal(in)
json.Unmarshal(inrec, &inInterface)
// iterate through inrecs
for field, val := range inInterface {
fmt.Println("KV Pair: ", field, val)
}
}
go playground here
Here is a function I've written in the past to convert a struct to a map, using tags as keys
// ToMap converts a struct to a map using the struct's tags.
//
// ToMap uses tags on struct fields to decide which fields to add to the
// returned map.
func ToMap(in interface{}, tag string) (map[string]interface{}, error){
out := make(map[string]interface{})
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
// we only accept structs
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("ToMap only accepts structs; got %T", v)
}
typ := v.Type()
for i := 0; i < v.NumField(); i++ {
// gets us a StructField
fi := typ.Field(i)
if tagv := fi.Tag.Get(tag); tagv != "" {
// set key of map to value in struct field
out[tagv] = v.Field(i).Interface()
}
}
return out, nil
}
Runnable example here.
Note, if you have multiple fields with the same tag value, then you will obviously not be able to store them all within a map. It might be prudent to return an error if that happens.
I like the importable package for the accepted answer, but it does not translate my json aliases. Most of my projects have a helper function/class that I import.
Here is a function that solves my specific problem.
// Converts a struct to a map while maintaining the json alias as keys
func StructToMap(obj interface{}) (newMap map[string]interface{}, err error) {
data, err := json.Marshal(obj) // Convert to a json string
if err != nil {
return
}
err = json.Unmarshal(data, &newMap) // Convert to a map
return
}
And in the main, this is how it would be called...
package main
import (
"fmt"
"encoding/json"
"github.com/fatih/structs"
)
type MyStructObject struct {
Email string `json:"email_address"`
}
func main() {
obj := &MyStructObject{Email: "test#test.com"}
// My solution
fmt.Println(StructToMap(obj)) // prints {"email_address": "test#test.com"}
// The currently accepted solution
fmt.Println(structs.Map(obj)) // prints {"Email": "test#test.com"}
}
package main
import (
"fmt"
"reflect"
)
type bill struct {
N1 int
N2 string
n3 string
}
func main() {
a := bill{4, "dhfthf", "fdgdf"}
v := reflect.ValueOf(a)
values := make(map[string]interface{}, v.NumField())
for i := 0; i < v.NumField(); i++ {
if v.Field(i).CanInterface() {
values[v.Type().Field(i).Name] = v.Field(i).Interface()
} else {
fmt.Printf("sorry you have a unexported field (lower case) value you are trying to sneak past. I will not allow it: %v\n", v.Type().Field(i).Name)
}
}
fmt.Println(values)
passObject(&values)
}
func passObject(v1 *map[string]interface{}) {
fmt.Println("yoyo")
}
I'm a bit late but I needed this kind of feature so I wrote this. Can resolve nested structs. By default, uses field names but can also use custom tags. A side effect is that if you set the tagTitle const to json, you could use the json tags you already have.
package main
import (
"fmt"
"reflect"
)
func StructToMap(val interface{}) map[string]interface{} {
//The name of the tag you will use for fields of struct
const tagTitle = "kelvin"
var data map[string]interface{} = make(map[string]interface{})
varType := reflect.TypeOf(val)
if varType.Kind() != reflect.Struct {
// Provided value is not an interface, do what you will with that here
fmt.Println("Not a struct")
return nil
}
value := reflect.ValueOf(val)
for i := 0; i < varType.NumField(); i++ {
if !value.Field(i).CanInterface() {
//Skip unexported fields
continue
}
tag, ok := varType.Field(i).Tag.Lookup(tagTitle)
var fieldName string
if ok && len(tag) > 0 {
fieldName = tag
} else {
fieldName = varType.Field(i).Name
}
if varType.Field(i).Type.Kind() != reflect.Struct {
data[fieldName] = value.Field(i).Interface()
} else {
data[fieldName] = StructToMap(value.Field(i).Interface())
}
}
return data
}
map := Structpb.AsMap()
// map is the map[string]interface{}

Resources