Golang接口到struct

时间:2017-06-28 14:53:45

标签: go struct interface

我有一个函数,其参数类型为interface {},类似于:

func LoadTemplate(templateData interface{}) {

在我的例子中,templateData是一个结构,但每次都有不同的结构。我使用了类型" interface {}"因为它允许我发送所有类型的数据。

我使用此templateData将数据发送到模板:

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

但是现在我想要添加一些新数据而我不知道该怎么做,因为"界面" type不允许我添加/追加任何内容。

我尝试将接口转换为结构,但我不知道如何将数据附加到结构未知的结构中。

如果我使用以下功能,我可以看到界面的数据:

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)?或者我如何将其转换为结构或其他内容以附加新的子/数据?

4 个答案:

答案 0 :(得分:10)

阿德里安是对的。更进一步,如果您知道实现该接口的类型,则只能对接口执行任何操作。空接口interface{}实际上并不像通常被误解的“任何”值;它只是一个立即满足所有类型的接口。

因此,您只能通过在添加前后知道满足空接口的类型来从中获取值或创建具有添加值的新“接口”。

在静态类型中,最接近你可以做到你想要的是通过在after类型中嵌入before类型,以便仍然可以在after类型的根处访问所有内容。以下说明了这一点。

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)
}

答案 1 :(得分:4)

您不能随意将数据附加到结构中;他们是静态打字的。您只能将值分配给为该特定结构类型定义的字段。你最好的选择可能是使用map代替结构。

答案 2 :(得分:1)

不推荐,但您可以使用反射包动态创建结构。

以下是一个例子:

package main

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目前不为嵌入字段生成包装器方法。在将来的版本中可能会解除此限制。

因此,您创建的结构将不会从嵌入类型继承方法。不过,也许它可以满足您的需求。

答案 3 :(得分:0)

如果您只是想将接口转换为struct,请使用此方法。

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
}