You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 00:45:03