欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang?json?庫(kù)中的RawMessage功能原理

 更新時(shí)間:2023年05月12日 14:09:53   作者:ag9920  
今天我們來(lái)學(xué)習(xí)一個(gè) Golang 官方 json 庫(kù)提供了一個(gè)經(jīng)典能力RawMessage,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

json 作為一種通用的編解碼協(xié)議,可閱讀性上比 thrift,protobuf 等協(xié)議要好一些,同時(shí)編碼的 size 也會(huì)比 xml 這類協(xié)議要小,在市面上用的非常多。甚至在很多業(yè)務(wù)上,我們的線上實(shí)例消耗最大的部分就是 json 的序列化和反序列化。這也是為什么很多 Gopher 會(huì)致力于研究怎樣最有效地優(yōu)化這個(gè)過(guò)程。

今天我們來(lái)學(xué)習(xí)一個(gè) Golang 官方 json 庫(kù)提供了一個(gè)經(jīng)典能力:RawMessage。

什么是序列化

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

參考 json 包中 Marshaler 和 Unmarshaler 兩個(gè)接口定義:

// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler interface {
?? ?MarshalJSON() ([]byte, error)
}
序列化,也就是 Marshal,需要將一種類型轉(zhuǎn)換為一個(gè)字節(jié)數(shù)組,也就是這里接口返回值的 []byte。
go復(fù)制代碼// 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
}

而反序列化,則是序列化的逆過(guò)程,接收一個(gè)字節(jié)數(shù)組,轉(zhuǎn)換為目標(biāo)的類型值。

事實(shí)上如果你對(duì)自定義的類型實(shí)現(xiàn)了上面兩個(gè)接口,調(diào)用 json 包的 json.Marshal 以及 json.Unmarshal 函數(shù)時(shí)就會(huì)執(zhí)行你的實(shí)現(xiàn)。

簡(jiǎn)言之,本質(zhì)上看,序列化就是將一個(gè) object 轉(zhuǎn)換為字節(jié)數(shù)組,即 []byte 的過(guò)程。
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 具體來(lái)講是 json 庫(kù)中定義的一個(gè)類型。它實(shí)現(xiàn)了 Marshaler 接口以及 Unmarshaler 接口,以此來(lái)支持序列化的能力。注意上面我們引用 官方 doc 的說(shuō)明。我們直接來(lái)看看源碼中的實(shí)現(xiàn):

// 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)

非常直接,其實(shí) RawMessage 底層就是一個(gè) []byte。序列化時(shí)是直接把自己 return 回去了。而反序列化時(shí)則是把入?yún)⒌?[]byte 拷貝一份,寫(xiě)入自己的內(nèi)存地址即可。

有意思了,前一節(jié)我們提到過(guò),序列化后產(chǎn)出的本來(lái)就是一個(gè) []byte,那為什么還要專門(mén)再搞一個(gè) RawMessage 出來(lái),有什么作用呢?

沒(méi)錯(cuò),RawMessage 其實(shí)人如其名,代表的就是一個(gè)終態(tài)。什么意思呢?我本來(lái)就是個(gè)字節(jié)數(shù)組,那么如果你要對(duì)我進(jìn)行序列化,就不需要什么成本,直接把我這個(gè)字節(jié)數(shù)組拿過(guò)去即可。如果要反序列化,沒(méi)事,你直接把原來(lái)的字節(jié)數(shù)組拿到就夠了。

這就是 Raw 的含義,原來(lái)是什么樣,現(xiàn)在就是什么樣。原樣拿過(guò)來(lái)即可。

這里參照 Using Go’s json.RawMessage 的經(jīng)典解釋。

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 看作是一部分可以暫時(shí)忽略的信息,以后可以進(jìn)一步去解析,但此時(shí)不用。所以,我們保留它的原始形式,還是個(gè)字節(jié)數(shù)組即可。

使用場(chǎng)景

軟件開(kāi)發(fā)中,我們經(jīng)常說(shuō)不要過(guò)度設(shè)計(jì),好的代碼應(yīng)當(dāng)有明確的使用場(chǎng)景,而且能高效地解決一類問(wèn)題,而不是在設(shè)想和概念上造出來(lái)一個(gè)未經(jīng)過(guò)驗(yàn)證的空中樓閣。
那么 RawMessage 是不是這樣一個(gè)空中樓閣呢?其實(shí)并不是。
我們可以將其當(dāng)做一個(gè)【占位符】。設(shè)想一下,我們給某種業(yè)務(wù)場(chǎng)景定義了一個(gè)通用的 model,其中部分?jǐn)?shù)據(jù)需要在不同場(chǎng)景下對(duì)應(yīng)不同的結(jié)構(gòu)體。這個(gè)時(shí)候怎么 Marshal 成字節(jié)數(shù)組,存入數(shù)據(jù)庫(kù),以及讀出數(shù)據(jù),還原出 model 呢?
我們就可以將這個(gè)可變的字段定義為 json.RawMessage,利用它適配萬(wàn)物的能力來(lái)進(jìn)行讀寫(xiě)。

復(fù)用預(yù)計(jì)算的 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 是我們臨時(shí)定義的結(jié)構(gòu)體,body 是明確的一個(gè)字符串,而 header 是可變的。

還記得么?RawMessage 本質(zhì)是個(gè) []byte,所以我們可以用

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

來(lái)將一個(gè)字符串轉(zhuǎn)換為 RawMessage。隨后對(duì)其進(jìn)行 Marshal,輸出的結(jié)果如下:

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

發(fā)現(xiàn)了么?

這里 "precomputed": true 跟我們構(gòu)造的 RawMessage 是一模一樣的,所以對(duì)應(yīng)到第一個(gè)能力:在序列化時(shí)使用一個(gè)預(yù)先計(jì)算好的 json 值。

延遲解析 json 結(jié)構(gòu)

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)
?? ?}
}

這里的例子其實(shí)更典型。Color 中的 Point 可能存在兩種結(jié)構(gòu)描述,一種是 RGB,另一種是 YCbCr,而我們對(duì)應(yīng)到底層存儲(chǔ),又希望能復(fù)用,這是非常常見(jiàn)的。
所以,這里采用了【兩級(jí)反序列化】的策略:

第一級(jí),解析出來(lái)公共字段,利用 json.RawMessage 延遲這部分差異字段的解析。

第二級(jí),根據(jù)已經(jīng)解析出來(lái)的字段(一般是有類似 type 的語(yǔ)義),判斷再次反序列化時(shí)要使用的結(jié)構(gòu),基于 json.RawMessage 再次 Unmarshal,拿到最終的數(shù)據(jù)。

上面的示例輸出結(jié)果如下:

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

總結(jié)

json 提供的 RawMessage 是直接暴露了底層的 []byte 作為交互憑證,它可以被內(nèi)嵌在各種結(jié)構(gòu)體中。作為不可變的字段類型的 placeholder,延遲解析。相較于 string 類型效率更高。從實(shí)現(xiàn)上看非常簡(jiǎn)單,只是封裝了一層字節(jié)數(shù)組的交互,大家可以放心使用。

以上就是Golang json 庫(kù)中的RawMessage功能原理的詳細(xì)內(nèi)容,更多關(guān)于Golang json庫(kù)RawMessage的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang爬蟲(chóng)框架 colly的使用

    Golang爬蟲(chóng)框架 colly的使用

    本文主要介紹了Golang爬蟲(chóng)框架 colly的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 自定義Go?Json的序列化方法譯文

    自定義Go?Json的序列化方法譯文

    這篇文章主要為大家介紹了自定義Go?Json序列化方法譯文,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • golang 流式讀取和發(fā)送使用場(chǎng)景示例

    golang 流式讀取和發(fā)送使用場(chǎng)景示例

    這篇文章主要為大家介紹了golang 流式讀取和發(fā)送使用場(chǎng)景示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • golang判斷兩個(gè)事件是否存在沖突的方法示例

    golang判斷兩個(gè)事件是否存在沖突的方法示例

    這篇文章主要為大家詳細(xì)介紹了golang判斷兩個(gè)事件是否存在沖突的方法示例,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-10-10
  • golang如何優(yōu)雅的編寫(xiě)事務(wù)代碼示例

    golang如何優(yōu)雅的編寫(xiě)事務(wù)代碼示例

    這篇文章主要介紹了golang如何優(yōu)雅的編寫(xiě)事務(wù)代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • go如何使用gin結(jié)合jwt做登錄功能簡(jiǎn)單示例

    go如何使用gin結(jié)合jwt做登錄功能簡(jiǎn)單示例

    jwt全稱Json web token,是一種認(rèn)證和信息交流的工具,這篇文章主要給大家介紹了關(guān)于go如何使用gin結(jié)合jwt做登錄功能的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • 深入分析Golang Server源碼實(shí)現(xiàn)過(guò)程

    深入分析Golang Server源碼實(shí)現(xiàn)過(guò)程

    這篇文章深入介紹了Golang Server源碼實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-02-02
  • Golang最大遞減數(shù)算法問(wèn)題分析

    Golang最大遞減數(shù)算法問(wèn)題分析

    這篇文章主要介紹了Golang最大遞減數(shù)算法問(wèn)題分析,結(jié)合實(shí)例形式分析了Go語(yǔ)言數(shù)字遍歷與運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下
    2017-01-01
  • Go語(yǔ)言中JSON文件的讀寫(xiě)操作

    Go語(yǔ)言中JSON文件的讀寫(xiě)操作

    本文主要介紹了Go語(yǔ)言JSON文件的讀寫(xiě)操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Go中各種newreader和newbuffer的使用總結(jié)

    Go中各種newreader和newbuffer的使用總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中各種newreader和newbuffer的使用的相關(guān)資料,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以了解下
    2023-11-11

最新評(píng)論