简体   繁体   English

如何使用 Go 中接口数组中的值初始化结构?

[英]How can I initialize struct with values from array of interface in Go?

I'm getting a message from server like [0,"on",[6,1,5,"market",45.7]] and save it to []interface{} variable.我从服务器收到一条消息,如 [0,"on",[6,1,5,"market",45.7]] 并将其保存到 []interface{} 变量。 I want to initialize struct with values of this array.我想用这个数组的值初始化结构。 I'm totally new in Go and try to do it like:我是 Go 的新手并尝试这样做:

import "golang.org/x/net/websocket"
...
var msg []interface{}
// Server send response: `[0,"on",[6,1,5,"market",45.7]]`
if err := websocket.Message.Receive(ws, &msg); err != nil {
    logger.Println(err)
} else {
    type Order struct {
        ID int32,
        GID int32,
        CID int32,
        Type string,
        Amount float64
    }
    
    // here msg is [0,"on",[6,1,5,"market",45.7]]
    
    switch msg[1] {
    case "on":
        if rawOrder, ok := msg[2].([]interface{}); ok {
            order := Order{int32(rawOrder[0]), int32(rawOrder[1]), int32(rawOrder[2]), string(rawOrder[3]), float64(rawOrder[4])}
        }
}

But I'm getting an error "Cannot convert an expression of the type 'interface{}' to the type 'int32'" and the next step is use switch for every rawOrder[i] type, but it's toooo long.但是我收到一个错误“无法将'interface{}'类型的表达式转换为'int32'类型”,下一步是对每个 rawOrder[i] 类型使用 switch,但它太长了。 How can I do it easilly?我怎样才能轻松做到?

If you know that the codec used on the websocket will always be json, you can formally define Order and give it an UnmarshalJSON function to do the decoding for you.如果您知道 websocket 上使用的编解码器将始终是 json,您可以正式定义Order并给它一个UnmarshalJSON函数来为您进行解码。

import "golang.org/x/net/websocket"

type Order struct {
    ID, GID, CID int32
    Type         string
    Amount       float64
}

func (o *Order) UnmarshalJSON(data []byte) error {
    var first []json.RawMessage
    err := json.Unmarshal(data, &first)
    if err != nil {
        return fmt.Errorf("invalid order, must be array: %w", err)
    }
    if len(first) != 3 {
        return fmt.Errorf("invalid order, length must be 3, got %d", len(first))
    }

    var second string
    err = json.Unmarshal(first[1], &second)
    if err != nil {
        return fmt.Errorf("invalid order, second element must be string: %w", err)
    }

    switch second {
    case "on":
        var third []json.RawMessage
        err = json.Unmarshal(first[2], &third)
        if err != nil {
            return fmt.Errorf("invalid order, third element must be array: %w", err)
        }

        if len(third) != 5 {
            return fmt.Errorf("invalid order, element 3 length must be 5, got %d", len(third))
        }

        for i, f := range []interface{}{&o.ID, &o.GID, &o.CID, &o.Type, &o.Amount} {
            err = json.Unmarshal(third[i], f)
            if err != nil {
                return fmt.Errorf("invalid order, wrong type for element 3[%d]: %w", i, err)
            }
        }
        return nil
    }

    return fmt.Errorf("invalid order, unknown type %q", second)
}

...
var msg *Order
// Server send response: `[0,"on",[6,1,5,"market",45.7]]`
if err := websocket.JSON.Receive(ws, &msg); err != nil {
    logger.Println(err)
}

// msg is now an &Order{ID:6, GID:1, CID:5, Type:"market", Amount:45.7}

}

The reason the UnmarshalJSON function is huge is because your API is bad. UnmarshalJSON函数很大的原因是你的 API 很糟糕。 If you control the Server;如果您控制服务器; then you should avoid using mixed types in the same array, and you should avoid using arrays for relational data.那么你应该避免在同一个数组中使用混合类型,并且你应该避免将数组用于关系数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM