Golang dynamic variable reference

In Go, I would like to do something like this. I have a large object with many structures (using google protobuf ). here is a far-fetched example:

 person.name = "testing" person.address.street = "123 test st" person.address.city = "tester" person.address.zip = 90210 person.billing.address.same = true 

I would like to be able to dynamically refer to things. eg:

 key := "person.address.zip" fmt.Println("the value of key: " + key) // would like to get 90210 key := "person.address.city" fmt.Println("the value of key: " + key) // would like to get "tester" 

Is this possible in Go? if so, how could I do this? Essentially, I am creating a report that contains only a subset of the object, and I want to be able to create a mapping file in which the user can map keys / values ​​together and my program will output the value. It works for me on python, but I wanted to try using Go :)

+5
source share
2 answers

You can use func (v Value) FieldByName(name string) Value from the reflect package:

FieldByName returns a struct field with the given name. It returns a null value if the field is not found. This is panic if v Kind is not a structure.

Like this working code example:

 package main import "fmt" import "reflect" func main() { person := Person{} person.name = "testing" person.address.street = "123 test st" person.address.city = "tester" person.address.zip = 90210 person.billing.address.same = true v := reflect.ValueOf(person) f := v.FieldByName("address") key := f.FieldByName("zip") fmt.Println(key) // 90210 fmt.Println(f.FieldByName("city")) // tester } type Person struct { name string address Address billing Billing } type Billing struct { address Address } type Address struct { street, city string zip int same bool } 

output:

 90210 tester 

And for your special occasion, you can use fmt.Println(field(person, "person.address.zip")) , as this working code example (just for demonstration):

 package main import "fmt" import "reflect" import "strings" func field(t interface{}, key string) reflect.Value { strs := strings.Split(key, ".") v := reflect.ValueOf(t) for _, s := range strs[1:] { v = v.FieldByName(s) } return v } func main() { person := Person{} person.name = "testing" person.address.street = "123 test st" person.address.city = "tester" person.address.zip = 90210 person.billing.address.same = true fmt.Println(field(person, "person.address.zip")) //90210 fmt.Println(field(person, "person.address.city")) //tester } type Person struct { name string address Address billing Billing } type Billing struct { address Address } type Address struct { street, city string zip int same bool } 

output:

 90210 tester 
+4
source

I am not familiar with the internal components of protobuf or if it provides any means for this.

But, (1) if you want to read the value in the form in which you described it, by dynamically chaining the fields and (2) you want to read it more than once; I just serialize it in json and use this package. It is very fast and gives you (almost) the same semantics that you desire:

 // assuming your object got marshaled to this for example json := `{"name":{"first":"Janet","last":"Prichard"},"age":47}` value := gjson.Get(json, "name.last") println(value.String()) 
+1
source

All Articles