Go json.Encoder.SetEscapeHTML(false) 什么时候适合关闭转义
当 JSON 作为独立数据交给 JSON 解析器消费,而且你希望输出中的 URL、文本片段保留可读的 &、、> 时,可以对当前 Encoder 调用 SetEscapeHTML(false)。如果只是担心默认转义会改变 URL 的含义,通常不必关闭:正常 JSON 解析会还原相同的字符串值。
官方地址:https://pkg.go.dev/encoding/json
把转义设置当成输出契约
这个开关适合放在“输出契约”中理解。业务值是 URL 或文本,JSON 则是它们的一种序列化表示。默认把某些字符写成 Unicode 转义,不意味着业务值被改写。把编码后的字节直接展示给人看,与把 JSON 解析后交给业务处理,是不同的使用方式。
例如 URL 参数之间的连接符,在默认 JSON 字节中可以表示为 \u0026,解析后仍然是 &。关闭 HTML 转义主要改变这类字符的表示方式,而不是修复一个解析错误。若消费方把整段 JSON 文字当成 URL 使用,应该纠正消费方式,而不是要求序列化器迁就错误的数据层次。
哪些场景需要保留可读字符
一个明确的场景是独立 JSON 导出文件:维护者会直接阅读、比较其中的链接和描述,频繁出现的转义让人工查看不便。另一个场景是协议明确要求某种字符表示,此时设置应放在拥有该输出协议的函数中,保持同一出口的字节风格一致。
不要因为某处不喜欢转义,就顺手改掉所有 JSON 出口。调用只影响这一 Encoder 实例及其后续编码,不会修改 json.Marshal 的默认设置,也不是进程级全局开关。创建新 Encoder 时,仍需要按该出口的要求显式配置。
| 消费方式 | 判断重点 |
|---|---|
| 独立 JSON 文件,常由人直接阅读 | 保留原字符是否更便于维护 |
| 标准 JSON 解析器读取字段 | 解析值一致,通常无须为 URL 关闭 |
| 要求固定字节的外部协议 | 遵循协议明确约定 |
| 输出还会被嵌入 HTML | 不能沿用独立 JSON 的决策;本文不展开模板处理 |

用同一个输入比较两种编码
下面用一个带查询参数的示例地址和一段标记文本比较输出。example.invalid 是示例用途,不涉及实际网络请求。代码保存为 main.go 后可在自己的 Go 环境运行;这里提供的是语义示例,不声称已执行。
package main
import (
"bytes"
"encoding/json"
"fmt"
"os"
)
type Payload struct {
Link string `json:"link"`
Text string `json:"text"`
}
// encode 仅配置当前编码器,保留 Encode 的末尾换行。
func encode(v Payload, escapeHTML bool) ([]byte, error) {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(escapeHTML)
if err := enc.Encode(v); err != nil {
return nil, fmt.Errorf("编码 JSON: %w", err)
}
return buf.Bytes(), nil
}
func run() error {
input := Payload{
Link: "https://example.invalid/search?a=1&b=2",
Text: "中文 ",
}
escaped, err := encode(input, true)
if err != nil { return err }
readable, err := encode(input, false)
if err != nil { return err }
// 分别解析,比较字符串值;不对编码后的字节做替换。
var left, right Payload
if err := json.Unmarshal(escaped, &left); err != nil {
return fmt.Errorf("解析默认编码: %w", err)
}
if err := json.Unmarshal(readable, &right); err != nil {
return fmt.Errorf("解析关闭转义的编码: %w", err)
}
fmt.Printf("默认编码:%s", escaped)
fmt.Printf("关闭转义:%s", readable)
fmt.Printf("字节相同:%v\n", bytes.Equal(escaped, readable))
fmt.Printf("值均还原:%v\n", left == input && right == input)
return nil
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}按标准编码语义,默认编码会对示例中的连接符和尖括号使用相应转义,关闭后保留这些字符。两份字节会有差异,而分别 Unmarshal 得到的 Link 与 Text 都应与 input 一致。后两项比较特意分开:字节不相等和业务值相等可以同时成立。
encode 接受布尔参数,使选择集中在序列化出口;调用方不用在结果上找字符串替换。代码也保留 Encode 的尾部换行。这个换行是 Encoder 的输出行为,与 HTML 转义开关是两个独立问题,关闭开关不会让 Encode 变成 Marshal 的逐字节替代品。
关闭 HTML 转义不会关闭 JSON 转义
名称中的 HTML 不能省略。SetEscapeHTML(false) 不会把字符串变成原样粘贴的裸文本。JSON 必须处理的双引号、反斜杠和控制字符仍然按 JSON 规则编码;中文原本通常就不需要因此改写。若诉求是“所有字符都不转义”,这个开关并不提供这样的模式。
当前 encoding/json 的字符串编码实现还对 U+2028 和 U+2029 保留转义处理。因此不能承诺“传 false 后不存在任何 Unicode 转义”。定位问题时,先确认看到的具体字符和实际字节,再判断它是否属于该开关控制的集合。
另一个反例是二次编码。若业务已经持有一段完整 JSON 文本,却把它作为普通 Go string 再 Encode,得到的是包含该文本的 JSON 字符串,内部引号自然需要转义。这与 HTML 字符转义无关。关闭开关不会把“JSON 字符串值”自动变回原先的对象。

字节不同会影响哪些下游处理
如果下游处理的是解析值,选择通常只影响可读性。如果处理的是编码后的原始字节,例如文件快照、摘要或协议中的签名输入,则表示差异就有实际影响。应在约定序列化方式后再计算,而不是计算后继续修改输出。
从 Marshal 改到 Encoder 时,还要把末尾换行和缩进一并纳入字节约定。这个开关本身也不等于规范化 JSON,更不能仅靠它保证不同语言的编码器输出完全相同。本文只处理普通字段的字符表示,不延伸自定义 MarshalJSON 的所有行为。
不要对整个结果全局替换转义序列。原始字符串里可能本来就包含反斜杠与字母组成的文字,盲替换容易把值改变。应在编码时选好参数;若已有 JSON,需要更改表示,也应先明确数据结构与重新编码要求。
决定开关前的几个具体问题
为什么客户端仍显示反斜杠?
先区分查看的是原始响应字节,还是已经解析后的字段。有些调试工具还会为展示再加一层转义。也要排除将已有 JSON 文本作为普通字符串再次编码的情况。
SetEscapeHTML(false) 会同时关闭缩进吗?
不会。缩进由 SetIndent 控制,末尾换行属于 Encode 的输出行为。想改变其中一项,就使用对应接口,别把它们绑成一个设置。
什么时候保留默认设置就足够?
消费方按 JSON 解析且没有特殊字节要求时,默认转义通常不妨碍业务值。只有明确需要改善独立 JSON 的可读性或满足输出契约时,再局部配置该 Encoder。
Go io.ReadFull 怎样处理固定长度协议的半包和 EOF
- 上一篇
- Go io.ReadFull 怎样处理固定长度协议的半包和 EOF
- 下一篇
- Go io.MultiReader 拼接多个文件时怎么判断中间文件已经读完
-
- Golang · Go问答 | 8分钟前 |
- Go sql.Null[T] 扫描可空字段时怎么判断 Valid
- 403浏览 收藏
-
- Golang · Go问答 | 9分钟前 |
- Go ServeMux 同时注册方法和路径模式时怎么判断匹配优先级
- 193浏览 收藏
-
- Golang · Go问答 | 10分钟前 | go · 数据库 · 工程实践 · Go database/sql 动态查询 ColumnType ScanType
- Go ColumnType.ScanType 怎么为动态查询选择扫描类型
- 110浏览 收藏
-
- Golang · Go问答 | 16分钟前 |
- Go ColumnType.Nullable 返回不支持时怎么兼容不同数据库驱动
- 394浏览 收藏
-
- Golang · Go问答 | 16分钟前 |
- Go io.MultiReader 拼接多个文件时怎么判断中间文件已经读完
- 329浏览 收藏
-
- Golang · Go问答 | 18分钟前 | go · eof Go io.ReadFull 固定长度协议 半包
- Go io.ReadFull 怎样处理固定长度协议的半包和 EOF
- 405浏览 收藏
-
- Golang · Go问答 | 21分钟前 | 标准库 · Go问答 · Go net.Conn 读取超时 bufio.ReadBytes 分隔符消息
- Go bufio.ReadBytes 遇到读超时后怎样继续拼接一条消息
- 390浏览 收藏
-
- Golang · Go问答 | 23分钟前 |
- Go png.FormatError 怎么区分图片损坏和格式不支持
- 368浏览 收藏
-
- Golang · Go问答 | 27分钟前 |
- Go png.Encoder 怎样选择 CompressionLevel 平衡大小和速度
- 325浏览 收藏
-
- Golang · Go问答 | 31分钟前 | 标准库 · go · Go 图像处理 image.Rectangle image.Point 坐标判断
- Go image.Point.In 怎样判断坐标是否落在图像矩形内
- 365浏览 收藏
-
- Golang · Go问答 | 36分钟前 |
- Go color.NRGBAModel 转换颜色时怎样避免 alpha 预乘
- 183浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 891次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 891次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 976次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 961次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 788次使用
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- Go netip.ParseAddr 为什么拒绝带端口字符串
- 2026-11-03 501浏览
-
- Go generate 未执行指令的注释与目录检查
- 2026-10-28 501浏览
-
- Go RWMutex 写锁等待过久的竞争分析
- 2026-10-28 501浏览

