web-dev-qa-db-ja.com

golangのreflect.Valueから基本的な値を取得する方法は?

そこで、Go(golang)でリフレクションを開始するのに役立つコードを見つけましたが、基本的な構造体とそのフィールドからmap[string]stringを作成できるように、基礎となる値を取得するのに苦労しています。

最終的には、結果をmap[string]interface{}にしたいと思いますが、この1つの問題が私を阻んでいます。

私が現在持っているコード:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    FirstName string `tag_name:"tag 1"`
    LastName  string `tag_name:"tag 2"`
    Age       int  `tag_name:"tag 3"`
}

func inspect(f interface{}) map[string]string {

    m := make(map[string]string)
    val := reflect.ValueOf(f).Elem()

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)

        f := valueField.Interface()
        val := reflect.ValueOf(f)
        m[typeField.Name] = val.String()
    }

    return m
}

func dump(m map[string]string) {

    for k, v := range m {
        fmt.Printf("%s : %s\n", k, v)
    }
}

func main() {
    f := &Foo{
        FirstName: "Drew",
        LastName:  "Olson",
        Age:       30,
    }

    a := inspect(f)

    dump(a)
}

コードの実行からの出力:

FirstName : Drew
LastName : Olson
Age : <int Value>

私が理解しているのは、FirstNameとLastNameの出力は実際のreflect.Valueオブジェクトですが、文字列の場合、valueのString()メソッドは基になるStringを出力するだけです。 intを取得して文字列に変更したいのですが、relfectパッケージのドキュメントからは、その方法がすぐにはわかりません。

Soo .... golangのreflect.Valueから基になる値を取得するにはどうすればよいですか?

30
lucidquiet

値を解析する方法の良い例は、fmtパッケージです。 このコード を参照してください。

上記のコードを使用して問題に一致させると、次のようになります。

switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    m[typeField.Name] = strconv.FormatInt(val.Int(), 10)
case reflect.String:
    m[typeField.Name] = val.String()    
// etc...
}

基本的に、すべての 利用可能な種類 を確認する必要があります。

14
nemo

順調に進んでいるようです。あなたのコードで私が見ている問題は、値について仮定することです。つまり、いつElem()を呼び出すか、そして何回(ポインタを解決するために)するかを意味します。これを知るには、reflect.Kindを見る必要があります。値はreflect.Ptrですか?次にElem()を使用します。

val.Interface()/val.String()/val.Int()の値を取得したら、必要に応じて値を変換できます。使用するものはreflect.Kindに依存します。 intstringとの間で変換するには、strconvパッケージを使用する必要があります。

encoding/jsonおよびencoding/xmlパッケージは、すでにこの種の作業を行っています。ソースコードはいくつかの素晴らしい例を提供します。たとえば、 encoding/xml/read.gocopyValueencoding/xml/marshal.gomarshalSimpleを見てください。 。

4
Luke

これは、Go 1.5(2015年8月)で行う方が簡単です。 review 8731 および commit 049b89d by Rob Pike(robpike を参照してください。

fmt:_reflect.Value_を特別に扱う-保持する値として

これにより、Reflect.Value()引数の実際の値を出力できます。

_reflect.Value_がPrintf(など)に渡されると、fmtStringメソッドを呼び出しましたが、そのメソッドはその内容を公開しません。
コンテンツを取得するために、Value.Interface()を呼び出すことができますが、Valueがエクスポートされていないか、禁止されている場合、これは違法です。

このCLは、fmtパッケージに些細な変更を加えることで状況を改善します。_reflect.Value_を引数として見ると、_reflect.Value_をパッケージ内で行うのとまったく同じように扱います。
これは、常にValueの内容をthatPrintfの引数であるかのように出力することを意味します。

これは間違いなく重大な変更ですが、これは真の改善であり、このパッケージからフォーマットされた出力に対して行った他の多くの微調整よりも重大な改善ではないと思います。

2
VonC