登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go json.MarshalIndent 的 prefix 与 indent 输出层级控制

来源:17golang原创

时间:2026-10-31 12:17:07 204浏览 收藏

我在给配置导出接口增加可读 JSON 时,最先踩到的坑不是编码失败,而是把 prefix 和 indent 当成了同一种空白。json.MarshalIndent 的输入是 Go 值:它先完成 JSON 编码,再把每个对象或数组元素按层级排版。prefix 是嵌套行的固定行首文本,indent 是随层级重复的缩进文本;两者职责不同,参数位置也不能互换。

官方文档地址:https://pkg.go.dev/encoding/json#MarshalIndent

实际使用时,接口响应和配置文件通常把 prefix 设为空字符串,只用两个空格或一个制表符作为 indent;只有日志分段、嵌入片段或需要明显标记行首时,才给 prefix 加固定文本。格式化只解决可读性,不负责字段排序、HTML 转义策略或修复非法数据。
  • prefix:每个嵌套结构行前附加的固定文本,不按深度增长。
  • indent:每深入一层就重复一次的缩进文本。
  • 返回值是新的 []byte;编码失败时必须先处理 error,再使用结果。

先把两个格式化参数放回各自的职责

MarshalIndent(v, prefix, indent) 与 Marshal(v) 一样遍历 Go 值,只是额外调用缩进逻辑。把 prefix 想成“每一行统一贴上的标签”,把 indent 想成“表示树深度的标尺”,就不容易写反。最外层结果不会因为 prefix 非空就自动在第一行前面多出一段前缀,前缀主要出现在嵌套元素的行首。

Go json.MarshalIndent 的 Go 值输入、prefix 固定行首和 indent 层级缩进之间的静态关系说明图
图1:结构说明图,展示 Go 值经过 MarshalIndent 后由 prefix 和 indent 分别控制行首与层级;不是运行截图。

用小对象观察嵌套层级如何展开

下面的代码选择空前缀和两个空格缩进,适合配置文件、调试日志或人工阅读的接口响应。示例只表达 API 的输出关系,不把代码块当成本机执行截图。

package main

import (
	"encoding/json"
	"fmt"
)

type Service struct {
	Name   string   `json:"name"`
	Ports  []int    `json:"ports"`
	Labels []string `json:"labels"`
}

func main() {
	value := Service{Name: "api", Ports: []int{8080, 8443}, Labels: []string{"internal", "stable"}}

	// 空 prefix 让结果适合直接保存为 JSON 文件或返回接口。
	data, err := json.MarshalIndent(value, "", "  ")
	if err != nil {
		// 编码错误不能被空字节结果掩盖,调用方应保留错误上下文。
		panic(err)
	}

	// indent 会随着对象和数组深度重复,帮助读者看出数据树结构。
	fmt.Println(string(data))
}

这里第二个参数是 "",第三个参数是 " "。如果把两个参数调换,输出仍可能是合法 JSON,但视觉层级会变成固定文本重复、缩进不明显的样子。生产代码中不建议为了“看起来有层次”把长日志标签放进 indent,这会让输出体积随嵌套深度增长。

按场景选择 prefix,别把日志标记混进接口协议

如果输出要被另一个 JSON 解析器消费,推荐保持 prefix 为空;任何行首装饰都可能让整体文本不再是一个可直接解析的 JSON 文档。日志里需要区分多个片段时,可以把格式化结果作为日志字段的值,或在 JSON 外层统一加日志上下文,而不是把 | 、时间戳这类字符塞进结构内容。

配置导出同样优先使用短缩进。两个空格通常比制表符更容易跨编辑器保持一致;大对象则要权衡可读性和传输体积,接口响应可以使用 Marshal 输出紧凑 JSON,把 MarshalIndent 留给调试、导出和人工审查。

MarshalIndent 在接口 JSON、配置文件和日志片段三种输出场景中的 prefix 与 indent 边界说明图
图2:边界说明图,区分接口协议的合法 JSON、配置文件的可读缩进和日志片段的外部上下文。

编码错误和相邻 API 的边界要单独处理

MarshalIndent 不能编码循环引用,某些值也可能触发 UnsupportedTypeError 或 UnsupportedValueError。不能只判断返回字节是否为空;应先检查 error,再把结果交给文件、HTTP 或日志层。若输出来自已有 JSON 字节,而不是 Go 值,应考虑 json.Indent;它负责重新排版输入文本,不负责把 Go 结构体转换成 JSON。

另一个常见误区是把缩进和 HTML 转义混为一谈。需要调整 <、>、& 的编码策略时,应使用 json.Encoder.SetEscapeHTML,不要通过修改 prefix 或 indent 解决。字段顺序也由输入类型和编码规则决定,不能把缩进输出当成排序 API。

目标优先 API关键判断
Go 值生成可读 JSONjson.MarshalIndent先编码,再按层级排版
已有 JSON 字节重新排版json.Indent输入必须先能被解析
控制 HTML 字符转义json.Encoder.SetEscapeHTML这是编码策略,不是缩进参数

Go json.MarshalIndent 参数常见问题

prefix 和 indent 哪个表示层级?

indent 表示层级,每深入一层就重复一次;prefix 是固定的行首文本,不承担深度计算。

接口返回 JSON 时可以设置 prefix 吗?

通常不建议。接口正文应保持一个可直接解析的 JSON 文档,固定行首字符更适合外部日志或人工查看场景。

MarshalIndent 能保证 map 的业务字段顺序吗?

不能把它当作业务排序工具。需要稳定输出时,应选用结构体或在编码前建立明确的数据模型,并单独验证字段顺序需求。

结语

把 prefix 看作行首标签、把 indent 看作树深度标尺,MarshalIndent 的参数就会清楚很多。接口优先紧凑合法,配置和调试优先可读;输入、转义、字段顺序和错误处理则交给对应 API,各自负责自己的边界。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>