json.Indent 生成的 JSON 看似无变化,根本原因是它仅对已合法解析的 JSON 字节流做格式化,不校验输入合法性;若输入非标准 JSON(如缺引号、未转义),结果不可预测,正确做法是先 json.Unmarshal 再 json.MarshalIndent。

为什么 json.Indent 生成的 JSON 有时看起来没变化?
常见现象是调用 json.Indent 后输出和原始 JSON 一模一样,甚至多出空格或换行。根本原因是:json.Indent 只对**已合法解析的 JSON 字节流**做格式化,它不校验输入是否为有效 JSON —— 如果传入的是未转义的字符串、缺少引号的键、或已带缩进的“伪 JSON”,结果不可预测。
正确做法是:先用 json.Unmarshal 解析原始字节,再用 json.MarshalIndent(更安全)或 json.Indent 格式化。后者需确保输入是 []byte 形式的、语法正确的 JSON 数据。
- ✅ 推荐用
json.MarshalIndent:一步完成序列化 + 缩进,避免中间状态污染 - ⚠️ 若必须用
json.Indent,输入必须是json.Marshal直接产出的紧凑字节,不能是手拼的字符串或含注释的文本 - ❌ 不要用
json.Indent处理从 HTTP 响应体直接读取的、未经验证的[]byte—— 可能 panic 或静默失败
json.MarshalIndent 的 prefix 和 indent 参数到底控制什么?
这两个参数常被误解:prefix 是每行开头的固定前缀(比如 " " 或 "→ "),indent 是嵌套一层所用的缩进符号(如 " " 表示两个空格,"\t" 表示 Tab)。它们不决定“是否缩进”,只决定缩进样式。
例如:json.MarshalIndent(v, "", " ") 输出标准两空格缩进;而 json.MarshalIndent(v, "> ", "\t") 会让每行以 > 开头,嵌套用 Tab 对齐 —— 这在调试日志中偶尔有用,但多数场景保持 "" 和 " " 即可。
- prefix 为空字符串
""最常用;非空时会出现在每一行开头,包括首行 - indent 用空格比 Tab 更安全:某些终端或日志系统对 Tab 渲染不一致
- 性能上,
json.MarshalIndent比json.Marshal+json.Indent略慢,但差异微乎其微(纳秒级),无需为这点性能放弃可读性
如何把已有的紧凑 JSON 字符串转成缩进格式?
典型场景:从 API 接收了一段紧凑 JSON(如 {"name":"alice","age":30}),想打印成易读格式。不能直接传给 json.Indent,因为它的第三个参数(prefix)会被误当作 indent 使用 —— 正确路径是:先 json.Unmarshal 成 interface{} 或具体结构体,再 json.MarshalIndent 回去。
raw := []byte(`{"name":"alice","age":30}`)
var v interface{}
if err := json.Unmarshal(raw, &v); err != nil {
log.Fatal(err)
}
out, err := json.MarshalIndent(v, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
// 输出:
// {
// "name": "alice",
// "age": 30
// }
- 如果原始数据很大,反复 Unmarshal/Marshal 有内存开销;此时可考虑用第三方库如
github.com/buger/jsonparser流式处理,但标准库方案最稳 - 不要跳过
json.Unmarshal步骤 —— 即使你“确定”输入合法,Go 的 JSON 解析器会规范化键顺序、字符串转义等,这是格式化可靠的前提 - 若原始数据是
string而非[]byte,记得用[]byte(s)转换,否则json.Unmarshal会报invalid character
json.Compact 的唯一用途是移除空格和换行吗?
是的,但它不是简单的字符串 trim。它会解析并重序列化,因此能安全处理带 Unicode 转义、嵌套引号、或不规则空白的 JSON —— 比正则替换或 strings.ReplaceAll 可靠得多。
典型用途是准备 JSON 用于网络传输(减小体积)或写入 Redis 等键值存储。注意:json.Compact 不改变语义,但会抹平所有格式信息,包括人眼可读的换行和缩进。
- 输入必须是合法 JSON 字节;非法输入会导致 panic(不像
json.Unmarshal返回 error) - 输出总是紧凑无空格,且键名按字典序重排(因为底层用了
map[string]interface{}解析再序列化) - 如果需要紧凑但保留原始键序(如 map 插入顺序),必须用结构体而非
interface{},或改用jsoniter等支持顺序的库
json.MarshalIndent 和 json.Compact 的行为边界很清晰,但容易混淆的点在于:它们都依赖 JSON 语法合法性,却不主动告诉你哪里错了 —— 错误往往在上游解析阶段就该捕获,而不是等到格式化时才暴露。


















