返回顶部
首页 > 资讯 > 后端开发 > GO >Golang json 库中的RawMessage功能原理
  • 812
分享到

Golang json 库中的RawMessage功能原理

Golang json库RawMessageGolang json库 2023-05-20 06:05:55 812人浏览 八月长安
摘要

目录正文什么是序列化使用场景复用预计算的 JSON 值延迟解析 json 结构总结正文 json 作为一种通用的编解码协议,可阅读性上比 thrift,protobuf 等协议要好一

正文

json 作为一种通用的编解码协议,可阅读性上比 thrift,protobuf 等协议要好一些,同时编码的 size 也会比 xml 这类协议要小,在市面上用的非常多。甚至在很多业务上,我们的线上实例消耗最大的部分就是 json 的序列化和反序列化。这也是为什么很多 Gopher 会致力于研究怎样最有效地优化这个过程。

今天我们来学习一个 golang 官方 json 库提供了一个经典能力:RawMessage。

什么是序列化

首先我们思考一下所谓序列化指的是什么呢?

参考 json 包中 Marshaler 和 Unmarshaler 两个接口定义:

// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler interface {
    MarshalJSON() ([]byte, error)
}
序列化,也就是 Marshal,需要将一种类型转换为一个字节数组,也就是这里接口返回值的 []byte。
go复制代码// Unmarshaler is the interface implemented by types
// that can unmarshal a JSON description of themselves.
// The input can be assumed to be a valid encoding of
// a JSON value. UnmarshalJSON must copy the JSON data
// if it wishes to retain the data after returning.
//
// By convention, to approximate the behavior of Unmarshal itself,
// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
type Unmarshaler interface {
    UnmarshalJSON([]byte) error
}

而反序列化,则是序列化的逆过程,接收一个字节数组,转换为目标的类型值。

事实上如果你对自定义的类型实现了上面两个接口,调用 json 包的 json.Marshal 以及 json.Unmarshal 函数时就会执行你的实现。

简言之,本质上看,序列化就是将一个 object 转换为字节数组,即 []byte 的过程。
RawMessage

RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding.

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

// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte
// MarshalJSON returns m as the JSON encoding of m.
func (m RawMessage) MarshalJSON() ([]byte, error) {
    if m == nil {
        return []byte("null"), nil
    }
    return m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
    if m == nil {
        return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
    }
    *m = append((*m)[0:0], data...)
    return nil
}
var _ Marshaler = (*RawMessage)(nil)
var _ Unmarshaler = (*RawMessage)(nil)

非常直接,其实 RawMessage 底层就是一个 []byte。序列化时是直接把自己 return 回去了。而反序列化时则是把入参的 []byte 拷贝一份,写入自己的内存地址即可。

有意思了,前一节我们提到过,序列化后产出的本来就是一个 []byte,那为什么还要专门再搞一个 RawMessage 出来,有什么作用呢?

没错,RawMessage 其实人如其名,代表的就是一个终态。什么意思呢?我本来就是个字节数组,那么如果你要对我进行序列化,就不需要什么成本,直接把我这个字节数组拿过去即可。如果要反序列化,没事,你直接把原来的字节数组拿到就够了。

这就是 Raw 的含义,原来是什么样,现在就是什么样。原样拿过来即可。

这里参照 Using Go’s json.RawMessage 的经典解释。

We can think of the raw message as a piece of infORMation that we decide to ignore at the moment. The information is still there but we choose to keep it in its raw form — a byte array.

我们可以把 RawMessage 看作是一部分可以暂时忽略的信息,以后可以进一步去解析,但此时不用。所以,我们保留它的原始形式,还是个字节数组即可。

使用场景

软件开发中,我们经常说不要过度设计,好的代码应当有明确的使用场景,而且能高效地解决一类问题,而不是在设想和概念上造出来一个未经过验证的空中楼阁。
那么 RawMessage 是不是这样一个空中楼阁呢?其实并不是。
我们可以将其当做一个【占位符】。设想一下,我们给某种业务场景定义了一个通用的 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)
}

这里 c 是我们临时定义的结构体,body 是明确的一个字符串,而 header 是可变的。

还记得么?RawMessage 本质是个 []byte,所以我们可以用

json.RawMessage(`{"precomputed": true}`)

来将一个字符串转换为 RawMessage。随后对其进行 Marshal,输出的结果如下:

{
    "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}

总结

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

以上就是Golang json 库中的RawMessage功能原理的详细内容,更多关于Golang json库RawMessage的资料请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: Golang json 库中的RawMessage功能原理

本文链接: https://lsjlt.com/news/213995.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Golang json 库中的RawMessage功能原理
    目录正文什么是序列化使用场景复用预计算的 json 值延迟解析 json 结构总结正文 json 作为一种通用的编解码协议,可阅读性上比 thrift,protobuf 等协议要好一...
    99+
    2023-05-20
    Golang json库RawMessage Golang json库
  • 解析Golang标准库中的常用功能
    Golang标准库中的常见功能解析 作为一种强大且高效的编程语言,Golang在其标准库中提供了众多常见功能。本文将详细解析几个常见的功能,并提供具体的代码示例。 一、文件操作 创建和写入文件 Golang的...
    99+
    2024-01-20
  • 解锁 Java JSON 处理的强大功能
    JSON 处理在 Java 中的重要性 JSON 是一种轻量级的数据交换格式,在 Java 应用程序中广泛应用于数据持久化、网络通信和配置管理。熟练掌握 JSON 处理技术至关重要,以确保应用程序与外部系统和设备的无缝集成。 流行的 J...
    99+
    2024-03-07
    JSON、Java、libGDX、解析、序列化、验证
  • 如何删除golang原始json输入中的“\”符号
    php小编小新教你如何删除golang原始json输入中的“\”符号。在处理golang原始json输入时,我们可能会遇到特殊字符“\”的问题,这会影响到数据的正确解析和使用。幸运的是...
    99+
    2024-02-09
  • Linux中 inotify功能的实现原理是什么
    今天就跟大家聊聊有关Linux中 inotify功能的实现原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1. inotify主要功能它是一个内核用于通知用户空间程序文件系统...
    99+
    2023-06-16
  • Java中常用的json库性能比较
    本篇内容介绍了“Java中常用的json库性能比较”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!用于比较的库用4中json库进行比较,分别是...
    99+
    2023-06-16
  • 深入探讨Golang实时更新功能的原理及实现机制
    Golang热更新原理浅析:探讨实时更新功能的实现机制,需要具体代码示例 随着软件的发展,实时更新功能成为了许多开发者和用户所期望的一个重要特性。Golang作为一门现代化的编程语言,自然也需要具备这样的能力...
    99+
    2024-01-20
    Golang 实时更新 热更新
  • golang中的gc原理是什么
    今天小编给大家分享一下golang中的gc原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解...
    99+
    2024-04-02
  • Golang中注释的功能与重要性
    Golang中注释的作用与重要性 引言: 在软件开发的过程中,注释是一项重要的工具。它们可以帮助开发人员更好地理解代码的逻辑和功能,促进团队合作和知识共享。本文将讨论在Golang中注释的作用和重要性,并通过...
    99+
    2024-01-29
  • Golang中的宏定义:功能与限制
    标题:Golang中的宏定义:功能与限制 在Golang中,宏定义是一种用来简化代码编写的技术手段,通过宏定义可以在编译阶段将特定的代码片段替换为预先定义好的代码块。虽然Golang并...
    99+
    2024-03-01
    功能 golang 宏定义 代码可读性
  • golang中iris框架的功能有哪些
    高性能:Iris框架是基于Golang语言开发,具有高性能和高效率的特点,能够处理大量并发请求。 灵活路由:Iris框架提供...
    99+
    2024-04-03
    golang iris
  • 浅析Golang中map的实现原理
    Golang是一门支持面向对象编程的编程语言,它拥有高效的内存管理机制和灵活的语法特性,被广泛用于服务器端开发、网络编程、云计算等领域。在Golang中,map是一种非常重要的数据结构,它可以存储键值对,并提供快速的查找和插入操作。本文将介...
    99+
    2023-05-14
    go语言 Golang map
  • Golang中channel的原理解读(推荐)
    数据结构 channel的数据结构在$GOROOT/src/runtime/chan.go文件下: type hchan struct { qcount uint ...
    99+
    2024-04-02
  • 详解php团购功能的实现原理
    随着电子商务的迅猛发展,网购已经成为我们生活中不可或缺的一部分。团购作为其中一种形式,也被越来越多的人所接受。在团购的背后,有着一些技术实现的原理。本文将介绍php团购功能实现原理。一、什么是团购团购就是一种商品或服务的销售方式,由一家或多...
    99+
    2023-05-14
  • Golang中编译器的原理是什么
    本篇文章给大家分享的是有关Golang中编译器的原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。首先先来认识以下go的代码源文件分类&...
    99+
    2024-04-02
  • Golang中 WaitGroup的实现原理是什么
    这篇文章给大家介绍Golang中 WaitGroup的实现原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1 前言WaitGroup是Golang应用开发过程中经常使用的并发控制技术。WaitGroup,可理解...
    99+
    2023-06-19
  • Golang中map的实现原理是什么
    这篇“Golang中map的实现原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Golang中map的实现原理是什么...
    99+
    2023-07-05
  • 探究Golang中同名方法的原理
    Golang是一种开放源代码的编译型编程语言,由谷歌公司开发,旨在提高程序员的生产力。其中的方法是Golang中的一种重要概念,它允许在特定类型上定义函数,这些函数称为方法。在Gola...
    99+
    2024-02-23
    方法重载 golang机制 同名方法
  • python中math库的功能有哪些
    数学常数:包括π(π)和自然对数的基础(e)。 数学函数:包括三角函数(如sin、cos、tan)、指数函数(如exp、log)和...
    99+
    2024-03-15
    python
  • 理解Golang中线程与协程的工作原理
    Golang中线程与协程的工作原理 在Go语言(Golang)中,线程和协程是非常重要的概念,它们是并发编程的基本组成部分。理解它们的工作原理对于开发高效的并发程序非常重要。本文将深入...
    99+
    2024-02-29
    golang 线程 协程 go语言 并发访问
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作