
当 WebSocket 接口返回的 JSON 中,某个字段(如 args[0])本身是经过 JSON 编码的字符串(即“JSON-in-JSON”),需分两步解码:先解析外层结构,再对内层字符串单独调用 json.Unmarshal。直接替换引号或反斜杠易破坏合法转义,不可靠。
当 websocket 接口返回的 json 中,某个字段(如 `args[0]`)本身是经过 json 编码的字符串(即“json-in-json”),需分两步解码:先解析外层结构,再对内层字符串单独调用 `json.unmarshal`。直接替换引号或反斜杠易破坏合法转义,不可靠。
在 Go 中处理类似 "args":["{"method":"chatMsg",...}"] 这类数据时,核心误区在于试图用字符串替换(如 strings.Replacer)强行“修复” JSON 格式。但原始数据本质是合法的双层 JSON 编码:外层 JSON 的 args 字段是一个字符串数组,而数组首项是一个被 JSON 编码过的字符串(即内层 JSON 被序列化为字符串字面量)。此时若错误地将 " 替换为 ",会破坏 HTML 属性中本应保留的转义(如 href="https://..." → href="https://..."),导致解析器误判结构,引发 invalid character 'h' after object key:value pair 等语法错误。
正确的做法是严格遵循 JSON 分层语义,执行两次独立解码:
- 第一层解码:将原始字节流解析为外层结构,其中 Args 字段声明为 []string(而非嵌套结构),确保 JSON 解码器原样读取字符串内容;
- 第二层解码:取出 m.Args[0],将其作为独立 JSON 字符串,再次调用 json.Unmarshal 解析为 arg 结构体。
以下是完整、健壮的实现示例:
package main
import (
"encoding/json"
"log"
)
type Main struct {
Name string `json:"name"`
Args []string `json:"args"` // 关键:此处必须为 []string,而非 []arg
}
type Arg struct {
Method string `json:"method"`
Params Par `json:"params"`
}
type Par struct {
Channel string `json:"channel,omitempty"`
Name string `json:"name,omitempty"`
NameColor string `json:"nameColor,omitempty"`
Text string `json:"text,omitempty"`
Time int64 `json:"time,omitempty"`
}
func main() {
// 模拟从 WebSocket 接收的原始 JSON 字符串
str := `{"name":"message","args":["{"method":"chatMsg","params":{"channel":"channel","name":"name","nameColor":"B5B11E","text":"<a href="%5C%5C%5C%22https://play.spotify.com/browse%5C%5C%5C%22" target='\"_blank\"'>https://play.spotify.com/browse</a>","time":1455397119}}"]}`
var m Main
if err := json.Unmarshal([]byte(str), &m); err != nil {
log.Fatal("第一层解码失败:", err)
}
if len(m.Args) == 0 {
log.Fatal("args 数组为空")
}
var args Arg
if err := json.Unmarshal([]byte(m.Args[0]), &args); err != nil {
log.Fatal("第二层解码失败:", err)
}
// 成功获取解析结果
log.Printf("方法: %s, 频道: %s, 文本: %s, 时间: %d",
args.Method, args.Params.Channel, args.Params.Text, args.Params.Time)
}
✅ 关键注意事项:
- 禁止字符串预处理:不要用 strings.ReplaceAll 或 Replacer 修改 JSON 字符串——它会破坏 HTML 属性中的合法转义(如 " 是必需的),导致语法错误;
- 结构体字段类型必须精确匹配:外层 Args 必须定义为 []string,否则 json 包会尝试直接解码为结构体,触发非法类型转换错误;
- 添加基础校验:解码后检查 Args 长度,避免索引越界;
- 错误处理不可省略:两层解码均需独立 if err != nil 判断,便于定位问题发生在哪一层。
这种两阶段解码模式是处理“JSON 字符串嵌套 JSON 对象”场景的标准实践,既符合 JSON 规范,又完全规避了手动字符串操作的风险,适用于所有类似 API(如 WebSocket 消息总线、RPC 响应包装等)。











