wake-up-neo.com

Golang-Schnittstelle zu Struktur

Ich habe eine Funktion, die einen Parameter mit dem Typ interface {} hat, etwa:

func LoadTemplate(templateData interface{}) {

In meinem Fall ist templateData eine Struktur, die jedoch jedes Mal eine andere Struktur hat. Ich habe den Typ "interface {}" verwendet, weil ich damit alle Arten von Daten senden kann.

Ich verwende diese templateData, um die Daten an die Vorlage zu senden:

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

Aber jetzt möchte ich ein paar neue Daten anhängen und weiß nicht, wie ich das tun soll, weil der Typ "interface" es mir nicht erlaubt, etwas hinzuzufügen/anzufügen. 

Ich habe versucht, die Schnittstelle in eine Struktur zu konvertieren, aber ich weiß nicht, wie man Daten an eine Struktur mit unbekannter Struktur anfügt. 

Wenn ich die folgende Funktion verwende, kann ich die Daten der Schnittstelle sehen:

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
}

Wie kann ich ein Kind an den ursprünglichen Schnittstellenparameter (templateData) anhängen? Oder wie kann ich es in eine Struktur oder etwas anderes umwandeln, um das neue Kind/die neuen Daten anzufügen?

5
Pascut

Adrian hat recht. Um einen Schritt weiter zu gehen, können Sie mit Schnittstellen nur dann etwas tun, wenn Sie den Typ kennen, der diese Schnittstelle implementiert. Die leere Schnittstelle interface{} ist nicht wirklich ein "irgendetwas" -Wert, wie er normalerweise missverstanden wird. Es ist nur eine Schnittstelle, die von allen Typen sofort erfüllt wird.

Daher können Sie nur Werte abrufen oder eine neue "Schnittstelle" mit hinzugefügten Werten erstellen, indem Sie den Typ kennen, der die leere Schnittstelle vor und nach dem Hinzufügen erfüllt.

Am ehesten können Sie das tun, was Sie angesichts der statischen Typisierung tun möchten, indem Sie den Vorher-Typ in den Nach-Typ einbetten, sodass auf alles noch im Stamm des Nach-Typs zugegriffen werden kann. Das Folgende veranschaulicht dies.

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

Da die Daten, die Sie an die Vorlage übergeben, nicht dazu erforderlich sind, den Typ anzugeben, können Sie außerdem eine anonyme Struktur verwenden, um etwas sehr ähnliches auszuführen.

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

Sie können Daten nicht willkürlich an eine Struktur anhängen. Sie sind statisch getippt. Sie können nur den Feldern Werte zuweisen, die für diesen bestimmten Strukturtyp definiert sind. Am besten verwenden Sie dafür wahrscheinlich eine map anstelle von structs.

4
Adrian

Nicht empfehlenswert, aber Sie können mit dem reflect-Paket dynamisch Strukturen erstellen.

Hier ist ein Beispiel:

paket Haupt

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

Sie könnten so etwas wie die Combine-Funktion verwenden, um eine beliebige Anzahl von Strukturen zusammenzufügen. Leider aus der Dokumentation :

StructOf generiert derzeit keine Wrapper-Methoden für eingebettete Felder. Diese Einschränkung kann in einer zukünftigen Version aufgehoben werden.

Daher erbt Ihre erstellte Struktur keine Methoden von den eingebetteten Typen. Vielleicht macht es doch das, was Sie brauchen.

1
chowey

Wenn Sie nur Ihre Schnittstelle in struct konvertieren möchten, verwenden Sie diese Methode.

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