英文:
How can I initialize struct with values from array of interface in Go?
问题
我正在从服务器收到像[0,"on",[6,1,5,"market",45.7]]这样的消息,并将其保存到[]interface{}变量中。我想用这个数组的值初始化结构体。
我完全是Go的新手,尝试像这样做:
import "golang.org/x/net/websocket"
...
var msg []interface{}
// 服务器发送的响应:`[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
}
// 这里的msg是[0,"on",[6,1,5,"market",45.7]]
switch msg[1] {
case "on":
if rawOrder, ok := msg[2].([]interface{}); ok {
order := Order{ID: int32(rawOrder[0]), GID: int32(rawOrder[1]), CID: int32(rawOrder[2]), Type: string(rawOrder[3]), Amount: float64(rawOrder[4])}
}
}
}
但是我得到了一个错误"无法将类型为'interface{}'的表达式转换为'type'的类型'int32'",下一步是为每个rawOrder[i]类型使用switch,但是太长了。
我该如何简化它?
英文:
I'm getting a message from server like [0,"on",[6,1,5,"market",45.7]] and save it to []interface{} variable. I want to initialize struct with values of this array.
I'm totally new in Go and try to do it like:
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.
How can I do it easilly?
答案1
得分: 0
如果你知道在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("无效的订单,必须是数组:%w", err)
}
if len(first) != 3 {
return fmt.Errorf("无效的订单,长度必须为3,实际为%d", len(first))
}
var second string
err = json.Unmarshal(first[1], &second)
if err != nil {
return fmt.Errorf("无效的订单,第二个元素必须是字符串:%w", err)
}
switch second {
case "on":
var third []json.RawMessage
err = json.Unmarshal(first[2], &third)
if err != nil {
return fmt.Errorf("无效的订单,第三个元素必须是数组:%w", err)
}
if len(third) != 5 {
return fmt.Errorf("无效的订单,第三个元素长度必须为5,实际为%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("无效的订单,第三个元素[%d]的类型错误:%w", i, err)
}
}
return nil
}
return fmt.Errorf("无效的订单,未知类型:%q", second)
}
...
var msg *Order
// 服务器发送的响应:`[0,"on",[6,1,5,"market",45.7]]`
if err := websocket.JSON.Receive(ws, &msg); err != nil {
logger.Println(err)
}
// msg 现在是一个 &Order{ID:6, GID:1, CID:5, Type:"market", Amount:45.7}
UnmarshalJSON
函数之所以如此庞大,是因为你的API设计不好。如果你控制服务器,应避免在同一个数组中使用混合类型,并且应避免使用数组来表示关联数据。
英文:
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.
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. 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论