
本文介绍在 Go 中无需反序列化或定义结构体,即可直接以可读格式输出 *json.RawMessage 类型数据的方法,核心是利用 fmt.Printf 的 %s 动作符正确解引用并显示原始 JSON 字符串。
本文介绍在 go 中无需反序列化或定义结构体,即可直接以可读格式输出 `*json.rawmessage` 类型数据的方法,核心是利用 `fmt.printf` 的 `%s` 动作符正确解引用并显示原始 json 字符串。
在使用 Elasticsearch 等动态 JSON API 时,Go 客户端(如 olivere/elastic 或 elastic/go-elasticsearch)常将文档源(_source)字段声明为 *json.RawMessage 类型——这是一种高效、零拷贝的延迟解析机制,但直接用 fmt.Println(hit.Source) 会输出类似 [123 34 110 97 109 101 34 58 34 74 111 104 110 34 125] 的字节切片,完全不可读。
根本原因在于:json.RawMessage 是 []byte 的别名,而 fmt.Println 对其指针值调用 String() 方法时,并不会自动转为 UTF-8 字符串;它只是按 []byte 默认格式打印底层字节。
✅ 正确做法是使用 fmt.Printf 配合 %s 动词:
for _, hit := range searchResult.Hits.Hits {
fmt.Printf("%s\n", *hit.Source) // 注意解引用:*hit.Source
}
⚠️ 关键细节:
- 必须显式解引用 *json.RawMessage(即 *hit.Source),否则 %s 作用于指针本身,仍可能输出地址或异常;
- %s 会将 []byte 按 UTF-8 编码解释为字符串,完美还原原始 JSON 文本;
- 若需美化输出(如缩进),可进一步结合 json.Indent:
var buf bytes.Buffer
err := json.Indent(&buf, *hit.Source, "", " ")
if err != nil {
fmt.Printf("invalid JSON: %v\n", err)
fmt.Printf("%s\n", *hit.Source) // fallback to raw
} else {
fmt.Println(buf.String())
}
? 小结:*json.RawMessage 本质是 *[]byte,打印时应优先考虑 fmt.Printf("%s", *raw) 而非 fmt.Println;这既轻量又符合 Go 的类型语义,是处理动态 JSON 日志、调试和原型开发的推荐实践。











