RawMessage 具体来讲是 json 库中定义的一个类型。它实现了 Marshaler 接口以及 Unmarshaler 接口,以此来支持序列化的能力。注意上面我们引用 官方 doc 的说明。

使用场景

设想一下,我们给某种业务场景定义了一个通用的 model,其中部分数据需要在不同场景下对应不同的结构体。这个时候怎么 Marshal 成字节数组,存入数据库,以及读出数据,还原出 model 呢?

我们就可以将这个可变的字段定义为 json.RawMessage,利用它适配万物的能力来进行读写。

复用预计算的 json 值

package main

import (
"encoding/json"
"fmt"
"os"
) func main() {
h := json.RawMessage(`{"precomputed": true}`) c := struct {
Header *json.RawMessage `json:"header"`
Body string `json:"body"`
}{Header: &h, Body: "Hello Gophers!"} b, err := json.MarshalIndent(&c, "", "\t")
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b) }

输出:

{
"header": {
"precomputed": true
},
"body": "Hello Gophers!"
}

这里 "precomputed": true 跟我们构造的 RawMessage 是一模一样的,所以对应到第一个能力:在序列化时使用一个预先计算好的 json 值。

延迟解析 json 结构

package main

import (
"encoding/json"
"fmt"
"log"
) func main() {
type Color struct {
Space string
Point json.RawMessage // delay parsing until we know the color space
}
type RGB struct {
R uint8
G uint8
B uint8
}
type YCbCr struct {
Y uint8
Cb int8
Cr int8
} var j = []byte(`[
{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
{"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}}
]`)
var colors []Color
err := json.Unmarshal(j, &colors)
if err != nil {
log.Fatalln("error:", err)
} for _, c := range colors {
var dst any
switch c.Space {
case "RGB":
dst = new(RGB)
case "YCbCr":
dst = new(YCbCr)
}
err := json.Unmarshal(c.Point, dst)
if err != nil {
log.Fatalln("error:", err)
}
fmt.Println(c.Space, dst)
}
}

这里的例子其实更典型。Color 中的 Point 可能存在两种结构描述,一种是 RGB,另一种是 YCbCr,而我们对应到底层存储,又希望能复用,这是非常常见的。

所以,这里采用了【两级反序列化】的策略:

  • 第一级,解析出来公共字段,利用 json.RawMessage 延迟这部分差异字段的解析。
  • 第二级,根据已经解析出来的字段(一般是有类似 type 的语义),判断再次反序列化时要使用的结构,基于 json.RawMessage 再次 Unmarshal,拿到最终的数据。

上面的示例输出结果如下:

YCbCr &{255 0 -10}
RGB &{98 218 255}

使用示例:

1)如果json串没有固定的格式导致不好定义与其相对应的结构体时,我们可以使用json.RawMessage原始字节数据保存下来 ,然后一层一层的进行解析:

type sendMsg struct {
Name string `json:"name"`
Info string `json:"info"`
} func jsonTest() {
jsonStr := `{"sendMsg":{"name":"louiswang","info":"这里是一条消息"},"say":"Hello"}`
var data map[string]json.RawMessage
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
fmt.Println("json 解析失败了:", err)
return
}
var saystr string
if err := json.Unmarshal(data["say"], &saystr); err != nil {
fmt.Println("RawMessage 解析失败了:", err)
return }
fmt.Printf("%#v-------%T\n", saystr, saystr)
var resMsg sendMsg
if err := json.Unmarshal(data["sendMsg"], &resMsg); err != nil {
fmt.Println("RawMessage 解析失败了(sendMsg):", err)
return }
fmt.Printf("%#v-----%T\n", resMsg, resMsg)
fmt.Println(resMsg.Name, resMsg.Info) }

总结

json 提供的 RawMessage 是直接暴露了底层的 []byte 作为交互凭证,它可以被内嵌在各种结构体中。作为不可变的字段类型的 placeholder,延迟解析。相较于 string 类型效率更高。从实现上看非常简单,只是封装了一层字节数组的交互,大家可以放心使用。

golang之json.RawMessage的更多相关文章

  1. Golang 处理 Json(二):解码

    golang 编码 json 还比较简单,而解析 json 则非常蛋疼.不像 PHP 一句 json_decode() 就能搞定.之前项目开发中,为了兼容不同客户端的需求,请求的 content-ty ...

  2. 48 【golang】json的效率

    本文将主要做如下几方面的测试: 1,构造一个[100]struct的数组,然后来测试它的json编码后的字符串 或者([]byte),首先关心它的功能是否正常: 2,在很早之前,我们在使用golang ...

  3. Go_14:GoLang中 json、map、struct 之间的相互转化

    1. golang 中 json 转 struct <1. 使用 json.Unmarshal 时,结构体的每一项必须是导出项(import field).也就是说结构体的 key 对应的首字母 ...

  4. Golang 处理 Json(一):编码

    JSON 是一种数据格式描述语言.以 key 和 value 构成的哈系结构,类似 Javascript 中的对象,python 中的字典.通常 json 格式的 key 是字符串,其值可以是任意类型 ...

  5. GoLang中 json、map、struct 之间的相互转化

    1. golang 中 json 转 struct <1. 使用 json.Unmarshal 时,结构体的每一项必须是导出项(import field).也就是说结构体的 key 对应的首字母 ...

  6. golang解析json报错:invalid character '\x00' after top-level value

    golang解析json报错:invalid character '\x00' after top-level value 手动复制字符串:{"files":["c:/t ...

  7. Golang - 处理json

    目录 Golang - 处理json 1. 编码json 2. 解码json Golang - 处理json 1. 编码json 使用json.Marshal()函数可以对一组数据进行JSON格式的编 ...

  8. golang webservice[ json Martini webframe]

    golang webservice[ json Martini webframe] https://github.com/brunoga/go-webservice-sample 自己修改了一下例子, ...

  9. Golang的json包

    encoding/json encoding/json是官方提供的标准json, 实现RFC 7159中定义的JSON编码和解码.使用的时候需要预定义struct,原理是通过reflection和in ...

  10. Golang解析json的几种方法

    Golang解析json的几种方法 概要 使用Golang调用其它平台API接口时总会被多层的json串给恶心到,我记录一下自己解析json的几种方法. 一.自带的json包 func JsonUnm ...

随机推荐

  1. JDK有用的新特性-Java Record

    目录 Java Record Record使用 Instance Methods 静态方法 Static Method Record 的构造方法 step1: 紧凑和定制构造方法 Record 与 与 ...

  2. spark 自定义 accumulator

    默认的accumulator 只是最简单的 int/float 有时候我需要一个map来作为accumulator 这样,就可以处理 <string, int>类型的计数了. 此外我还需要 ...

  3. 6.23 Web日志分析&php&自动化工具

    应急响应的目的:保护阶段.分析.复现.修复.建议 分析出攻击时间,攻击操作,攻击结果,安全修复等给出合理方案: 知识点 熟悉常见web安全攻击技术 熟悉日志启用及存储查看 熟悉日志中记录数据库分类及分 ...

  4. Istio实现sidecar自动注入

    Istio实现sidecar自动注入 Sidecar模式 在Sidecar部署方式中,你会为每个应用的容器部署一个伴生容器.对于Service Mesh,Sidecar接管进出应用程序容器的所有网络流 ...

  5. JavaScript – ES Module

    前言 关于 JavaScript Modular 的多种版本和历史看这篇. 参考: 阮一峰 – Module 的语法 阮一峰 – Module 的加载实现 Export 语法 逐个 export 在想 ...

  6. SQL Server – Schema

    前言 久仰 dbo 大名, 但是一直没有认真去看它有啥作用. 今天翻看了一下 SQL Server sample database: AdventureWorks2019, 发现它用了许多 Schem ...

  7. Electron加载服务器端JS:高效打造跨平台桌面应用与本地 API 交互

    在现代桌面应用开发中,使用 Electron 加载远程服务器托管的前端资源,再与本地 API 交互,能够带来灵活的部署和强大的本地功能支持.这种方式不仅提升了开发效率,还能充分利用 PC 端的资源和性 ...

  8. 提升软件测试效率与灵活性:探索Mock测试的重要性

    Mock测试是测试过程中的一种方法,用于替代那些难以构造或获取的对象,通过创建虚拟对象来进行测试.所谓难以构造的对象如何理解呢? 举例来说,像HttpServletRequest这样的对象需要在具有s ...

  9. nginx服务器下laravel项目无法访问

    nginx服务器下laravel项目无法访问 后台用的nginx服务器,之前在本地开发项目时用的apache服务器,没想到切换到线上访问时除了首页一直显示404的错误,网页无法访问,网上搜索发现是ng ...

  10. /proc/vmalloc

    root@pita2_mr813_tina35:/# cat /proc/vmallocinfo 0xffffff80007d0000-0xffffff8000902000 1253376 load_ ...