Go语言中json.RawMessage

在 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 输出。

三、注意事项

  1. 类型转换 :json.RawMessage 与 []byte 可以直接转换(因为是类型别名):

    go 复制代码
    raw := json.RawMessage([]byte(`{"a":1}`))
    b := []byte(raw) // 合法
  2. 空值处理 :未初始化的 json.RawMessage 是 nil,序列化后为 null。

  3. 解析校验 :使用 json.Unmarshal 解析 json.RawMessage 时,需确保原始字节是有效的 JSON,否则会返回错误。

总结

json.RawMessage 的核心价值是保留 JSON 原始字节,延迟或手动控制解析,适用于:

  • 动态 JSON 结构(字段类型不固定)。
  • 嵌套 JSON 的直接嵌入。
  • 避免循环引用或复杂结构的自动解析问题。

通过它可以灵活处理 JSON 序列化/反序列化的细节,尤其在处理不确定结构的 JSON 数据时非常实用。

相关推荐
2601_966949655 分钟前
为什么不应该把 QuantDash API Key 直接写进公开代码仓库?从 Git 提交历史理解凭证泄露风险
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
智_永无止境6 分钟前
又找到一个纯Java的OCR项目,识别的格式还挺多
java·开发语言·ocr
小灰灰搞电子8 分钟前
Rust+Slint 实现动态流光边框源码分享
开发语言·前端·rust·流光边框
包小黑23 分钟前
Python 转 EXE (实用工具)
开发语言·python
我命由我1234537 分钟前
6-3.Python 异常处理 - 异常处理进阶(else 块与 finally 块、异常传递、自定义异常)
开发语言·前端·数据库·后端·python·学习·java-ee
额额额对了41 分钟前
【项目实战】从IMX6ULL环境监测到N647 AI视觉加速
linux·开发语言·嵌入式硬件
熊猫钓鱼>_>1 小时前
端侧 NPU 轻量大模型推理接入笔记【鸿蒙心迹】
开发语言·笔记·华为·ai编程·harmonyos·鸿蒙
外收内放1 小时前
FastAPI猜字谜实战(8):收官
开发语言·python·学习·fastapi
爱敲代码的TOM1 小时前
10.27 易购软件 Java开发线下一面
java·开发语言
学逆向的1 小时前
C++ 别名命名空间
开发语言·c++·网络安全