Go:小工具打印结构体,可以直接用 `json.MarshalIndent`

40 次浏览5 条回复

写本地小工具时,有时候只是想把一个结构体或 map 打得好看一点,不用先接日志库。标准库的 json.MarshalIndent 就够用。

环境:Go 1.20+。保存成 marshal-indent-demo.go:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    data := map[string]any{
        "name": "mesh",
        "tags": []string{"dev", "tool"},
        "ok": true,
    }

    b, err := json.MarshalIndent(data, "", "  ")
    if err != nil {
        panic(err)
    }

    fmt.Println(string(b))
}

跑一下:

go run marshal-indent-demo.go

输出会是缩进过的 JSON,临时排查配置、接口 mock 数据这种场景挺顺手。

要注意 map 的 key 顺序别拿来做业务判断;真要稳定快照,最好还是用结构体或者单独整理输出。

这个写法很适合一次性打印。

如果输出目标本来就是文件或 stdout,也可以用 json.NewEncoder(w) 再 SetIndent("", " "),少一步把整段 JSON 放进 []byte。小数据无所谓,配置比较大时会更顺手一点。

还有个小坑是默认会转义 HTML 字符,比如 < 会变成 \u003c。如果只是人眼看日志,通常没影响;但想贴出来更接近原文,可以用 Encoder 配 SetEscapeHTML(false)。

不过这样就更像输出展示了,别直接拿去拼 HTML 页面就行。

补一个细节:MarshalIndent 不是所有东西都能打,比如 func、chan、循环引用这类会直接报错。

所以临时调试时我一般还是保留 err 输出,不然看到空结果还挺容易误判。

Ryan小满Lv1#3

补一个细节:MarshalIndent 不是所有东西都能打,比如 func、chan、循环引用这类会直接报错。

所以临时调试时我一般还是保留 err 输出,不然看到空结果还挺容易误判。

还有一点容易忘:结构体里的小写字段不会被编码出来。

比如本来想临时打印排查,结果字段名没导出,输出里就像“丢字段”了一样。要给人看得清楚,还是用大写字段,再按需要补 json:"xxx" 这种 tag。

HosuLv1#4

还有一点容易忘:结构体里的小写字段不会被编码出来。

比如本来想临时打印排查,结果字段名没导出,输出里就像“丢字段”了一样。要给人看得清楚,还是用大写字段,再按需要补 json:"xxx" 这种 tag。

再补个偏实用的:如果只是调试配置,omitempty 有时反而会碍事。

零值字段被省掉后,看输出会分不清是“没填”还是“填了默认值”。这种临时打印我一般会少用它,等对外输出接口再考虑精简字段。