
本文介绍使用 json.RawMessage 在 Go 中直接嵌入已序列化的 JSON 片段,实现零开销聚合多个 JSON 对象,无需反复 unmarshal/marshal,避免字符串转义问题。
本文介绍使用 `json.rawmessage` 在 go 中直接嵌入已序列化的 json 片段,实现零开销聚合多个 json 对象,无需反复 unmarshal/marshal,避免字符串转义问题。
在构建微服务或数据聚合网关时,常需从多个 API 或消息源收集 JSON 数据,并合并为统一响应。若对每个子 JSON 字符串先 json.Unmarshal 成结构体再 json.Marshal 回整体,不仅性能损耗显著(尤其高频或大数据量场景),还易引入类型冗余与维护成本。Go 标准库提供的 json.RawMessage 正是为此类需求设计的轻量级解决方案。
json.RawMessage 是 []byte 的别名,同时实现了 json.Marshaler 和 json.Unmarshaler 接口。它能“透明”地保留原始 JSON 字节序列,在最终序列化时直接插入目标 JSON 对象中,既不解析也不转义——这正是解决“已序列化 JSON 被双重编码”的关键。
以下是一个典型聚合场景的完整示例:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
package main
import (
"encoding/json"
"fmt"
)
type Event struct {
ID int `json:"id"`
Timestamp string `json:"timestamp"`
Place json.RawMessage `json:"place"`
Attendees json.RawMessage `json:"attendees"`
Metadata json.RawMessage `json:"metadata,omitempty"`
}
func main() {
// 模拟从不同服务获取的原始 JSON 字符串(可来自 HTTP 响应、Kafka 消息等)
placeJSON := `{"address":"123 Main St","city":"San Francisco","geo":{"lat":37.7749,"lng":-122.4194}}`
attendeesJSON := `[{"name":"Alice","role":"speaker"},{"name":"Bob","role":"attendee"}]`
metadataJSON := `{"source":"api-v2","version":"1.3"}`
event := Event{
ID: 101,
Timestamp: "2024-06-15T09:30:00Z",
Place: json.RawMessage(placeJSON),
Attendees: json.RawMessage(attendeesJSON),
Metadata: json.RawMessage(metadataJSON),
}
data, err := json.Marshal(event)
if err != nil {
panic(err)
}
fmt.Println(string(data))
// 输出(格式化后):
// {
// "id": 101,
// "timestamp": "2024-06-15T09:30:00Z",
// "place": {"address":"123 Main St","city":"San Francisco","geo":{"lat":37.7749,"lng":-122.4194}},
// "attendees": [{"name":"Alice","role":"speaker"},{"name":"Bob","role":"attendee"}],
// "metadata": {"source":"api-v2","version":"1.3"}
// }
}✅ 优势总结:
- 零拷贝聚合:json.RawMessage 直接持有字节切片,避免中间结构体转换;
- 类型安全:字段仍受 Go 类型系统约束,编译期可检错;
- 灵活兼容:支持任意合法 JSON(对象、数组、字符串、数字、布尔、null);
- 按需解析:若后续需操作某子对象,仍可对其 RawMessage 单独 Unmarshal 到对应结构体。
⚠️ 注意事项:
- 确保传入的 JSON 字符串语法严格合法,否则 json.Marshal 会静默失败(实际在 Marshal 时才校验,但错误发生在最终序列化环节);建议在赋值前用 json.Valid() 验证:
if !json.Valid([]byte(placeJSON)) { return errors.New("invalid place JSON") } - json.RawMessage 不支持指针语义(如 *json.RawMessage),若字段可选且需区分“空”与“null”,应结合 omitempty 标签与显式 nil 赋值;
- 不要将 json.RawMessage 误用于动态拼接非 JSON 字符串(如 "{" + key + ":" + value + "}"),这极易破坏 JSON 结构完整性——始终使用合法 JSON 字面量或经 json.Marshal 生成的字节。
通过 json.RawMessage,你能在保持代码简洁性的同时,获得接近底层的 JSON 处理效率。它是 Go 生态中被低估却极为实用的“胶水类型”,特别适合构建高性能 API 网关、事件聚合器或配置桥接层。

















