Golang 动态变量参考

Golang dynamic variable reference

在围棋中,我想做这样的事情。我有一个包含许多结构的大对象(使用 Google 的 protobuf)。这是一个人为的例子:

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

我希望能够动态引用事物。例如:

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"

这在 Go 中可行吗?如果是这样,我该怎么做?本质上,我正在创建一个仅包含对象子集的报告,我希望能够创建一个映射文件,用户可以在其中将 keys/values 映射在一起,我的程序将输出该值。我在 python 中工作,但想尝试使用 Go :)

您可以使用 reflect 包中的 func (v Value) FieldByName(name string) Value

FieldByName returns the struct field with the given name. It returns the zero Value if no field was found. It panics if v's Kind is not struct.

喜欢这个工作示例代码:

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
}

输出:

90210
tester

对于你的特殊情况,你可以使用 fmt.Println(field(person, "person.address.zip")),就像这个工作示例代码(仅用于演示):

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
}

输出:

90210
tester

我不熟悉 protobuf 的内部结构,或者它是否提供任何方法来做到这一点。

但是,(1) 如果您想以您描述的方式读取值 - 通过动态链接字段和 (2) 你想多读一遍;我只是将其序列化为 json 并使用 this 包。它速度非常快,并且(几乎)为您提供了您想要的相同语义:

// 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())