当 url 编码的字符串内部还嵌套了 json 转义(如 `"%22%7b...%7d%22"`),需先 `url.queryunescape`,再用 `json.unmarshal` 两次:第一次解析出原始 json 字符串,第二次解析为结构体。
在 Go 开发中,常会遇到从 HTTP 查询参数、表单字段或第三方接口接收到的「双重编码」JSON 字符串——即整个 JSON 文本被 JSON 序列化后,再经 URL 编码(如 " → %22,{ → %7B,\ → %5C)。直接对解码后的字符串调用 json.Unmarshal 会失败,因为此时字节切片实际表示的是一个带外层引号的 JSON 字符串字面量(例如 "{\"key\":\"value\"}"),而非可直接映射的 JSON 对象。
正确的处理流程是两步解码:
示例代码如下:
package main
import (
"encoding/json"
"fmt"
"net/url"
)
type Info struct {
SessionId string `json:"sessionId"`
UserId int `json:"userId"`
UserName string `json:"userName"`
UserEmail string `json:"userEmail"`
UserRoles []string `json:"userRoles"`
}
func main() {
// 原始双重编码字符串(URL 编码 + JSON 字符串字面量)
encoded := "%22%7B%5C%22sessionId%5C%22%3A%5C%225331b937-7b55-4c2d-798a-25e574a7e8af%5C%22%2C%5C%22userId%5C%22%3A2%2C%5C%22userName%5C%22%3A%5C%22datami_op%5C%22%2C%5C%22userEmail%5C%22%3A%5C%22datami_op%40example.com%5C%22%2C%5C%22userRoles%5C%22%3A[%5C%22operator%5C%22]%7D%22"
// 步骤 1:URL 解码
s, err := url.QueryUnescape(encoded)
if err != nil {
panic(err)
}
// 步骤 2:首次 JSON 解码 → 得到纯净 JSON 字符串
var rawJSON string
if err := json.Unmarshal([]byte(s), &rawJSON); err != nil {
panic(fmt.Sprintf("first unmarshal failed: %v", err))
}
// 步骤 3:二次 JSON 解码 → 映射到结构体
var i Info
if err := json.Unmarshal([]byte(rawJSON), &i); err != nil {
panic(fmt.Sprintf("second unmarshal failed: %v", err))
}
fmt.Printf("%+v\n", i)
// 输出:{SessionId:"5331b937-7b55-4c2d-798a-25e574a7e8af" UserId:2 UserName:"datami_op" UserEmail:"datami_op@example.com" UserRoles:[operator]}
}✅ 关键注意事项:
通过两阶段反序列化,Go 程序能健壮、安全地处理各类嵌套编码的 JSON 数据,是 API 集成与网
