使接口进入结构

I'm a little new to golang and trying to process some websocket data.

I get data in the form of:

type Event struct {
    Event string      `json:"event"`
    Data  interface{} `json:"data"`
}

And the Data field is a json object that I want to process and get into the following struct:

type OrderBook struct {
    Pair      string          `json:"pair"`
    Timestamp string          `json:"timestamp"`
    Broker    string          `json:"broker"`
    Bids      []OrderBookItem `json:"bids"`
    Asks      []OrderBookItem `json:"asks"`
}

type OrderBookItem struct {
    Price  float64
    Amount float64
}

from the websocket function:

....
case "data":
    publish(e.Data) <--- this is where the error occurs
....

which calls:

func publish(data OrderBookResult) {
    log.Println("Publishing trade data...", data)

    o := &OrderBook{}
    o.Pair = "BTCEUR"
    o.Timestamp = data.Timestamp
    o.Broker = "Bitstamp"
    o.Asks = data.Asks
    o.Bids = data.Bids

}

The error in the websocket function I get is the following:

cannot use e.Data (type interface {}) as type OrderBookResult in argument to publish: need type assertion

How can I "cast" the websocket struct into the new struct and reference fields, that are not defined on the websocket struct. I have a node.js background and I haven't gotten my head around the strictness of go yet.

Thx

As others have noted type assertions would be the answer to solve your immediate error given the interface contains the right type of struct.

But if your interface comes from JSON parsing, it will likely contain a map[string]interface{} and not a OrderBookResult. See here for why that is the case.

To solve that please read up on how to create a custom JSON marshaller. For example in this blog. Below I have also included a way of processing a map[string]interface{} to get to your data in the publish function.

So you can either type assert before you call:

....
case "data":

    // Assert that e.Data is in fact a struct of type OrderBookResult
    if dataAsOrderBookResult, ok := e.Data.(OrderBookResult); ok {
        publish(dataAsOrderBookResult)
    } else {
        // handle error etc
        ...
    }

....

Or change the signature of your publish function and type assert the parameter inside:

func publish(data interface{}) {
    log.Println("Publishing trade data...", data)

    o := &OrderBook{}
    // Type switch
    switch v := data.(type) {
    case OrderBookResult:
        o.Pair = "BTCEUR"
        o.Timestamp = v.Timestamp
        o.Broker = "Bitstamp"
        o.Asks = v.Asks
        o.Bids = v.Bids
    case map[string]interface{}:
        o.Pair = "BTCEUR"
        o.Timestamp = v["Timestamp"].(string)
        o.Broker = "Bitstamp"
        // here again you need to type assert correctly and it may be a map again
        //o.Asks = v["Asks"].(OrderBookItem)
        //o.Bids = v["Bids"].(OrderBookItem)
    default:
        // error handling etc.
        ...
    } 
...    
}