在 Go 语言中,json.RawMessage 是标准库 encoding/json 包提供的一种类型,用于保存原始的 JSON 字节数据,避免在序列化/反序列化过程中被自动解析。它的核心作用是"延迟解析"或"保留原始 JSON 格式",常用于处理动态 JSON 结构、嵌套 JSON 或需要手动控制解析时机的场景。
一、json.RawMessage 的定义
json.RawMessage 本质上是 []byte 的类型别名:
go
type RawMessage []byte
但它实现了 json.Marshaler 和 json.Unmarshaler 接口,因此在 JSON 处理时会被特殊对待------序列化时直接输出原始字节,反序列化时直接保存原始 JSON 字节而不解析。
二、核心用法场景
1. 保留原始 JSON 数据,避免自动解析
当 JSON 中某字段的结构不固定(例如动态类型、嵌套 JSON),可以用 json.RawMessage 暂存原始字节,后续再根据业务逻辑手动解析。
示例 :
假设有一个 JSON 数据,其中 data 字段的结构可能是对象、数组或其他类型:
json
{
"type": "user",
"data": {"id": 1, "name": "Alice"}
}
或
json
{
"type": "post",
"data": {"id": 100, "title": "Go 教程"}
}
用 json.RawMessage 保存 data 字段的原始 JSON:
go
package main
import (
"encoding/json"
"fmt"
)
// 定义外层结构体,data 字段用 RawMessage 保存原始 JSON
type Response struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"` // 不自动解析,保留原始字节
}
// User 和 Post 是 data 可能的结构
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
}
func main() {
// 示例 JSON 数据(user 类型)
jsonStr := `{"type":"user","data":{"id":1,"name":"Alice"}}`
// 反序列化到 Response,data 保留原始 JSON 字节
var resp Response
if err := json.Unmarshal([]byte(jsonStr), &resp); err != nil {
panic(err)
}
// 根据 type 字段手动解析 data
switch resp.Type {
case "user":
var user User
if err := json.Unmarshal(resp.Data, &user); err != nil { // 手动解析 RawMessage
panic(err)
}
fmt.Printf("解析到用户:%+v\n", user) // 解析到用户:{ID:1 Name:Alice}
case "post":
var post Post
json.Unmarshal(resp.Data, &post)
fmt.Printf("解析到文章:%+v\n", post)
}
}
2. 嵌套 JSON 结构的序列化
当需要手动构造嵌套 JSON 时,json.RawMessage 可以直接嵌入原始 JSON 字节,避免多层结构体嵌套的繁琐。
示例 :
直接将原始 JSON 字符串作为字段值序列化:
go
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 原始 JSON 字符串(作为嵌套数据)
rawData := `{"key": "value", "num": 123}`
// 构造外层结构体,嵌入 RawMessage
data := struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data json.RawMessage `json:"data"` // 直接嵌入原始 JSON
}{
Code: 200,
Msg: "success",
Data: json.RawMessage(rawData), // 转换为 RawMessage
}
// 序列化
result, err := json.MarshalIndent(data, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(result))
}
输出:
json
{
"code": 200,
"msg": "success",
"data": {
"key": "value",
"num": 123
}
}
如果不用 json.RawMessage,直接用 string 类型保存 rawData,序列化后会带引号(作为字符串处理),不符合预期。
3. 避免循环引用导致的序列化错误
如果结构体中存在循环引用(如 A -> B -> A),直接序列化会报错。此时可以用 json.RawMessage 手动处理循环部分的 JSON 输出。
三、注意事项
-
类型转换 :
json.RawMessage与[]byte可以直接转换(因为是类型别名):goraw := json.RawMessage([]byte(`{"a":1}`)) b := []byte(raw) // 合法 -
空值处理 :未初始化的
json.RawMessage是nil,序列化后为null。 -
解析校验 :使用
json.Unmarshal解析json.RawMessage时,需确保原始字节是有效的 JSON,否则会返回错误。
总结
json.RawMessage 的核心价值是保留 JSON 原始字节,延迟或手动控制解析,适用于:
- 动态 JSON 结构(字段类型不固定)。
- 嵌套 JSON 的直接嵌入。
- 避免循环引用或复杂结构的自动解析问题。
通过它可以灵活处理 JSON 序列化/反序列化的细节,尤其在处理不确定结构的 JSON 数据时非常实用。