
当已有合法 jsON 格式的字符串(如 “{“key1”:{“key2″:”value1”}}”)时,不应再次用 json.Marshal 或 json.Encoder.Encode 编码,否则会导致双重转义;直接写入原始字节即可确保客户端收到标准 JSON。
当已有合法 json 格式的字符串(如 `”{“key1”:{“key2″:”value1”}}”`)时,不应再次用 `json.marshal` 或 `json.encoder.encode` 编码,否则会导致双重转义;直接写入原始字节即可确保客户端收到标准 json。
在 Go Web 开发中,一个常见误区是:误将已格式化好的 JSON 字符串当作普通 Go 值进行序列化。例如,你手动生成或从外部接收了一个符合 JSON 语法的字符串:
str := "{"key1":{"key2":"value1","key3":"value2"}}"
此时,str 的类型是 String,其内容本身已是合法 JSON 文本(注意:它不是 Go 的 map 或 Struct,只是一个字符串字面量)。若错误地执行:
err := json.NewEncoder(w).Encode(str) // ❌ 错误!
json.Encoder.Encode 会将该字符串视为 Go 字符串值,并将其作为 JSON 字符串类型(带双引号和转义)再次编码,最终响应体变为:
"{"key1":{"key2":"value1","key3":"value2"}}"
即:外层多了一对引号和全部转义——这在客户端解析时会失败(JSON.parse() 得到的是字符串而非对象)。
✅ 正确做法是跳过序列化步骤,直接以 application/json 内容类型写入原始字节:
str := "{"key1":{"key2":"value1","key3":"value2"}}" // 设置正确的 Content-Type 头(强烈建议) w.Header().Set("Content-Type", "application/json; charset=utf-8") // 直接写入原始 JSON 字节 _, err := w.Write([]byte(str)) if err != nil { http.Error(w, "Failed to write response", http.StatusInternalServerError) return }
⚠️ 注意事项:
- 务必设置 Content-Type: application/json:否则浏览器或客户端可能无法正确识别响应为 JSON;
- 确保输入字符串本身是严格合法的 JSON:可使用 json.Valid([]byte(str)) 预校验,避免因非法格式导致前端解析失败;
- 不要混用 json.Marshal + w.Write:若你本意是将 Go 结构体转 JSON,请直接 json.Marshal 后写入;若已有 JSON 字符串,则无需再 Marshal;
- 避免 json.Unmarshal → json.Marshal 的无意义往返:例如先解析成 Interface{} 再重 Marshal,既低效又易引入精度丢失(如浮点数、空 interface{} 类型推断偏差)。
总结:Go 的 json 包设计面向「Go 值 → JSON 字节」的序列化。当你已持有 JSON 字节流(表现为字符串),就进入了「传输层」范畴——此时应交由 HTTP 响应体直接承载,而非二次序列化。简洁、高效、语义准确,才是 API 响应的最佳实践。