9

在 Go 中,我想做这样的事情。我有一个包含许多结构的大对象(使用 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 中可能吗?如果是这样,我该怎么做?本质上,我正在创建一个仅包含对象子集的报告,并且我希望能够创建一个映射文件,用户可以在其中将键/值映射在一起,并且我的程序将输出该值。我在 python 中有这个工作,但想尝试使用 Go :)

4

2 回答 2

7

您可以func (v Value) FieldByName(name string) Valuereflect包中使用:

FieldByName 返回具有给定名称的结构字段。如果未找到任何字段,则返回零值。如果 v 的 Kind 不是结构,它会恐慌。

像这个工作示例代码:

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
于 2016-08-14T03:42:13.353 回答
1

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

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

// 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())
于 2016-08-14T11:28:20.843 回答