2016-08-13 7 views
5

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でこの作業を持っていますが、あなたがfunc (v Value) FieldByName(name string) Valuereflectからパッケージ使用することができます行く:)

+0

はい、可能です。それはあなたのパッケージを反映しています。リフレクションの欠点は、通常、大きな仕事量です。一方、リフレクションなしでは、html/templateやtext/templateのような完璧なパッケージは書くことができませんが、writtingのスタイルは(Pythonのような)インタプリタ言語のようです。 – lofcek

+0

と反映が遅い、最後の手段である必要があります。 – OneOfOne

+4

このようなものが本当に必要な場合は、構造体の値の階層ではなく、(ネストされた)マップを使用する必要があります。 – kostix

答えて

4

を使用して試してみたかった:

FieldByNameは、指定された名前を持つ構造体のフィールドを返します。フィールドが見つからなかった場合は、ゼロ値を に返します。 vの種類が 構造体でない場合、パニックになります。

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 
1

は私がいる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())