web-dev-qa-db-ja.com

構造体へのGolangインターフェイス

タイプinterface {}のパラメーターを持つ関数があります。

func LoadTemplate(templateData interface{}) {

私の場合、templateDataは構造体ですが、毎回構造が異なります。すべての種類のデータを送信できるため、タイプ「interface {}」を使用しました。

このtemplateDataを使用して、データをテンプレートに送信しています。

err := tmpl.ExecuteTemplate(w, baseTemplateName, templateData)

しかし、今は新しいデータを追加したいのですが、「インターフェース」タイプでは何も追加/追加できないため、その方法はわかりません。

インターフェイスを構造体に変換しようとしましたが、構造が不明な構造体にデータを追加する方法がわかりません。

次の関数を使用すると、インターフェイスのデータを確認できます。

templateData = appendAssetsToTemplateData(templateData)

func appendAssetsToTemplateData(t interface{}) interface{} {
    switch reflect.TypeOf(t).Kind() {
    case reflect.Struct:
        fmt.Println("struct")
        s := reflect.ValueOf(t)
        fmt.Println(s)

        //create a new struct based on current interface data
    }

    return t
}

最初のインターフェイスパラメータ(templateData)に子を追加する方法はありますか?または、新しい子/データを追加するために、どのように構造体または他の何かに変換できますか?

7
Pascut

エイドリアンは正しいです。さらに一歩進んで、インターフェイスを実装する型がわかっている場合にのみ、インターフェイスで何でもできます。空のインターフェイス、interface{}は、一般的に誤解されているような「何でも」の値ではありません。これは、すべてのタイプがすぐに満足するインターフェイスです。

したがって、値を取得するか、追加の前後に空のインターフェースを満たすタイプを知ることで、値を追加した新しい「インターフェース」を作成することができます。

静的な型付けを考えると、やりたいことを行うのに最も近いのは、前の型を後の型に埋め込むことです。これにより、後の型のルートですべてにアクセスできます。以下にこれを示します。

https://play.golang.org/p/JdF7Uevlqp

package main

import (
    "fmt"
)

type Before struct {
    m map[string]string
}

type After struct {
    Before
    s []string
}

func contrivedAfter(b interface{}) interface{} {
    return After{b.(Before), []string{"new value"}}
}

func main() {
    b := Before{map[string]string{"some": "value"}}
    a := contrivedAfter(b).(After)
    fmt.Println(a.m)
    fmt.Println(a.s)
}

さらに、テンプレートに渡すデータではタイプを指定する必要がないため、匿名の構造体を使用して非常によく似た処理を実行できます。

https://play.golang.org/p/3KUfHULR84

package main

import (
    "fmt"
)

type Before struct {
    m map[string]string
}

func contrivedAfter(b interface{}) interface{} {
    return struct{
        Before
        s []string
    }{b.(Before), []string{"new value"}}
}

func main() {
    b := Before{map[string]string{"some": "value"}}
    a := contrivedAfter(b)
    fmt.Println(a)
}
12
TheHerk

構造体にデータを勝手に追加することはできません。静的に型付けされています。その特定の構造タイプに定義されたフィールドにのみ値を割り当てることができます。おそらく、これには構造体の代わりにmapを使用することをお勧めします。

4
Adrian

推奨されていませんが、reflectパッケージを使用して構造体を動的に作成できます。

以下に例を示します。

パッケージメイン

import (
    "encoding/json"
    "os"
    "reflect"
)

type S struct {
    Name string
}

type D struct {
    Pants bool
}

func main() {
    a := Combine(&S{"Bob"}, &D{true})
    json.NewEncoder(os.Stderr).Encode(a)
}

func Combine(v ...interface{}) interface{} {
    f := make([]reflect.StructField, len(v))
    for i, u := range v {
        f[i].Type = reflect.TypeOf(u)
        f[i].Anonymous = true
    }

    r := reflect.New(reflect.StructOf(f)).Elem()
    for i, u := range v {
        r.Field(i).Set(reflect.ValueOf(u))
    }
    return r.Addr().Interface()
}

上記のCombine関数のようなものを使用して、任意の数の構造体を一緒にシュムッシュできます。残念ながら、 documentation から:

StructOfは現在、埋め込みフィールドのラッパーメソッドを生成しません。この制限は、将来のバージョンで解除される可能性があります。

したがって、作成された構造体は、埋め込み型からメソッドを継承しません。それでも、多分それはあなたが必要とすることをします。

1
chowey

インターフェイスを構造体に変換するだけの場合は、このメソッドを使用します。

type Customer struct {
    Name string `json:"name"`
}

func main() {
    // create a customer, add it to DTO object and marshal it
    receivedData := somefunc() //returns interface

    //Attempt to unmarshall our customer
    receivedCustomer := getCustomerFromDTO(receivedData)
    fmt.Println(receivedCustomer)
}

func getCustomerFromDTO(data interface{}) Customer {
    m := data.(map[string]interface{})
    customer := Customer{}
    if name, ok := m["name"].(string); ok {
        customer.Name = name
    }
    return customer
}
0
user8202594