go中json.marshal默认启用html转义(、&转为\u003c\u003e\u0026),需用json.encoder并调用setescapehtml(false)禁用,不可对已序列化json字符串使用htmlescape。

encoding/json.HTMLEscape 不是一个可直接调用的函数,它只是个辅助工具函数,作用对象是 *bytes.Buffer 或其他实现了 io.Writer 的目标,不能用于已生成的 JSON 字符串。
如果你看到类似 “用 HTMLEscape 处理 json.Marshal 结果” 的说法,那基本是误解——它不接收 []byte 或 string,也不修改已有 JSON 数据。
HTMLEscape 的真实用途:在写入时实时转义
HTMLEscape 是一个写入器包装器,只在 JSON 流式写入(比如写到 HTTP 响应、文件或 buffer)过程中,对字符串字段中的 、<code>>、& 做 Unicode 转义(\u003c、\u003e、\u0026)。
它和 json.Encoder 配合使用,且仅影响后续写入内容:
- 它不会改变 struct tag、不会影响
json.Marshal - 它不处理已存在的 JSON 字节,只干预 Encoder 输出流
buf := &bytes.Buffer{}
json.HTMLEscape(buf, []byte(`{"content":"<div>&test"}`)) // ❌ 错误用法:传入已编码 JSON
<p>正确用法是:</p>
<pre class="brush:php;toolbar:false;">buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(true) // 默认就是 true,这行可省
enc.Encode(data) // ✅ 此时才触发转义逻辑
注意:SetEscapeHTML(true) 是默认行为,所以多数情况下你根本不用显式设置。
为什么不该用 HTMLEscape 手动处理 json.Marshal 输出?
-
json.Marshal返回的是完整 JSON 字节切片,而HTMLEscape要求输入是「未转义的原始字符串字节」(如[]byte{"),不是 JSON 编码后的结构 - 如果你把
json.Marshal的结果喂给HTMLEscape,它会把整个 JSON 字符串(包括{、"、:)当成普通文本去扫描和转义,结果不可控,大概率破坏 JSON 格式 - 实际项目中,这种混用会导致:
-
"\u003c"变成"\u003c"(双重转义) -
"&"被错误识别为&并转义成\u0026 - 最终 JSON 解析失败
-
替代方案:按场景选对方法
-
你要输出 API JSON(浏览器/前端 JS 消费)
关闭转义更合理:- 用
json.Encoder+SetEscapeHTML(false) - 避免
json.Marshal后再做任何字符串替换(易漏、难维护)
- 用
-
你要把 JSON 嵌入 HTML 的
<script></script>标签里
确实需要转义,但应由模板引擎(如html/template)完成,而不是手动调HTMLEscape- Go 模板自动转义
js类型值,比手动拼接安全得多 - 不要把
json.Marshal结果用{{.RawJSON | safeJS}}这类方式绕过模板转义
- Go 模板自动转义
你真要临时转义某段字符串(非整个 JSON)
直接用标准库:html.EscapeString,它专为这个设计,语义清晰、无歧义
HTMLEscape 的存在意义,是让 json.Encoder 在流式输出时能与 HTML 上下文共存;它不是通用字符串处理器,也不是 Marshal 的补丁。混淆这两者,是线上 JSON 字段莫名多出 \u0026 的最常见原因。











