<video id="vtaxu"></video>
          <cite id="vtaxu"></cite>

          1. <video id="vtaxu"><menu id="vtaxu"></menu></video>

            <rt id="vtaxu"></rt>
          2. <rt id="vtaxu"><menuitem id="vtaxu"></menuitem></rt>
              <rp id="vtaxu"><menu id="vtaxu"></menu></rp>
              溫馨提示×

              溫馨提示×

              您好,登錄后才能下訂單哦!

              密碼登錄×
              登錄注冊×
              其他方式登錄
              點擊 登錄注冊 即表示同意《億速云用戶服務條款》

              Golang?json?庫中的RawMessage功能原理介紹

              發布時間:2023-11-07 15:15:05 來源:億速云 閱讀:108 作者:栢白 欄目:開發技術
              專為編程打造,自動寫代碼機器人,免費開通

              今天小編給大家分享的是Golang json 庫中的RawMessage功能原理介紹,相信很多人都不太了解,為了讓大家更加了解,所以給大家總結了以下內容,一起往下看吧。一定會有所收獲的哦。

              正文

              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&rsquo;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 &mdash; 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功能原理介紹就分享到這里了,希望以上內容可以對大家有一定的參考價值,可以學以致用。如果喜歡本篇文章,不妨把它分享出去讓更多的人看到。

              向AI問一下細節

              免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

              AI

              福利一区二区三区视频播放观看|国产精品无码一二区不卡免费|国产日韩欧美一区二区久久精品|精品91自产拍在线观看二区