Go语言如何反序列化带公共type字段的多种结构JSON消息
Go解析带公共type字段的多结构JSON最简方案
核心思路
所有消息都携带公共type字段,我们可以通过先解析公共字段判断类型、再反序列化对应结构体的方式实现需求,也可以通过延迟解析优化性能。
步骤1:定义消息结构体
先定义公共消息头和各类型专属结构体:
import ( "encoding/json" "fmt" "strings" "net" "bufio" ) // 公共消息头,仅用于提取type字段 type MessageHeader struct { Type string `json:"type"` } // 身份请求消息:type=newidentity 带identity字段 type NewIdentityReq struct { Type string `json:"type"` Identity string `json:"identity"` } // 身份响应消息:type=newidentity 带approved字段 type NewIdentityResp struct { Type string `json:"type"` Approved string `json:"approved"` } // 房间列表消息:type=roomlist type RoomList struct { Type string `json:"type"` Rooms []string `json:"rooms"` } // 创建房间请求:type=createroom type CreateRoomReq struct { Type string `json:"type"` RoomID string `json:"roomid"` }
步骤2:实现通用解析函数
该函数输入JSON字符串,直接返回消息类型、对应结构体实例和错误信息:
func ParseMessage(jsonStr string) (msgType string, msgInstance interface{}, err error) { // 第一步:仅解析公共头获取消息类型 var header MessageHeader if err = json.Unmarshal([]byte(jsonStr), &header); err != nil { return "", nil, err } msgType = header.Type // 第二步:按类型反序列化到对应结构体 switch msgType { case "newidentity": // 同type多结构场景,先转临时map判断字段 var temp map[string]interface{} if err = json.Unmarshal([]byte(jsonStr), &temp); err != nil { return "", nil, err } if _, hasIdentity := temp["identity"]; hasIdentity { var req NewIdentityReq if err = json.Unmarshal([]byte(jsonStr), &req); err != nil { return "", nil, err } msgInstance = req } else if _, hasApproved := temp["approved"]; hasApproved { var resp NewIdentityResp if err = json.Unmarshal([]byte(jsonStr), &resp); err != nil { return "", nil, err } msgInstance = resp } case "roomlist": var list RoomList if err = json.Unmarshal([]byte(jsonStr), &list); err != nil { return "", nil, err } msgInstance = list case "createroom": var req CreateRoomReq if err = json.Unmarshal([]byte(jsonStr), &req); err != nil { return "", nil, err } msgInstance = req default: return "", nil, fmt.Errorf("unsupported message type: %s", msgType) } return msgType, msgInstance, nil }
步骤3:业务逻辑中使用
在连接处理函数中直接调用即可,注意不要在循环中重复创建bufio.Reader,避免数据丢失:
func handleConnection(c net.Conn) { defer c.Close() reader := bufio.NewReader(c) for { netData, err := reader.ReadString('\n') if err != nil { fmt.Println(err) return } // 去除首尾空白字符(换行、空格等) jsonStr := strings.TrimSpace(netData) if jsonStr == "" { continue } // 调用解析函数 msgType, msg, err := ParseMessage(jsonStr) if err != nil { fmt.Printf("parse message error: %v\n", err) continue } // 类型断言处理对应业务逻辑 switch msgType { case "newidentity": switch v := msg.(type) { case NewIdentityReq: fmt.Printf("收到用户身份请求: %s\n", v.Identity) // 身份校验逻辑 case NewIdentityResp: fmt.Printf("收到身份校验结果: %s\n", v.Approved) } case "roomlist": list := msg.(RoomList) fmt.Printf("房间列表: %v\n", list.Rooms) case "createroom": req := msg.(CreateRoomReq) fmt.Printf("创建房间请求: %s\n", req.RoomID) } } }
性能优化方案
如果需要减少JSON解析次数,可以用json.RawMessage实现延迟解析,全程只需要解析一次JSON:
func ParseMessageV2(jsonStr []byte) (msgType string, msgInstance interface{}, err error) { // 定义带RawMessage的临时结构,仅解析公共type字段,剩余内容暂存不解析 var rawMsg struct { Type string `json:"type"` json.RawMessage } if err = json.Unmarshal(jsonStr, &rawMsg); err != nil { return "", nil, err } msgType = rawMsg.Type // 后续反序列化直接用暂存的RawMessage,无需重新解析全量JSON switch msgType { case "newidentity": var temp map[string]interface{} if err = json.Unmarshal(rawMsg.RawMessage, &temp); err != nil { return "", nil, err } // 剩余逻辑和之前完全一致,仅把反序列化输入替换为rawMsg.RawMessage // 其他类型处理逻辑同理 } return msgType, msgInstance, nil }
内容的提问来源于stack exchange,提问作者Lahiru Udayanga
相关产品推荐
相关产品推荐

