golang 中序列化方案

本文主要介绍二进制协议gob及msgpack的基本使用。
Go语言中的json包在序列化空接口存放的数字类型(整型、浮点型等)都序列化成float64类型。

Json

  • 从下面的例子就可以看出 json 序列化的时候会将空接口存放的数字类型(整型、浮点型等)都序列化成float64类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    func jsonDemo() {
    test1 := map[string]interface{}{
    "count": 1,
    }

    data1, err := json.Marshal(test1)
    if err != nil {
    log.Fatal(err)
    return
    }

    fmt.Println(data1)

    test2 := make(map[string]interface{})

    if err := json.Unmarshal(data1, &test2); err != nil {
    log.Fatal(err)
    return
    }

    for _, v := range test2 {
    fmt.Printf("value : %v, type = %T\n", v, v)
    }
    }
  • 输出的结果为:

  • 可以看出原本 int 类型的 interface{} 被转化成为了 float64

    1
    2
    [123 34 99 111 117 110 116 34 58 49 125]
    value : 1, type = float64

Gob

  • 标准库gob是golang提供的“私有”的编解码方式,它的效率会比json,xml等更高,特别适合在Go语言程序间传递数据。

  • 但是它序列化之后的结果会比json要大一些

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    func gobDemo() {
    test1 := map[string]interface{}{
    "count": int64(12),
    }

    // encode
    buf := new(bytes.Buffer)
    enc := gob.NewEncoder(buf)
    err := enc.Encode(test1)
    if err != nil {
    log.Fatal(err)
    }
    b := buf.Bytes()
    fmt.Println(b)

    test2 := make(map[string]interface{})

    dec := gob.NewDecoder(bytes.NewBuffer(b))
    err = dec.Decode(&test2)
    if err != nil {
    log.Fatal(err)
    }
    for _, v := range test2 {
    fmt.Printf("value : %v, type = %T\n", v, v)
    }
    }
  • 输出的结果为:

    1
    2
    [14 255 129 4 1 2 255 130 0 1 12 1 16 0 0 20 255 130 0 1 5 99 111 117 110 116 5 105 110 116 54 52 4 2 0 24]
    value : 12, type = int64

使用gob的一些特殊情况

  • 在使用gob序列化一些特殊的数据结构(例如:[]interface{})的时候,使用前需要先注册,使用的时候需要特别注意这一点
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package main

    import (
    "bytes"
    "encoding/gob"
    "fmt"
    "log"
    )

    func main() {
    // 如果这一行被注释掉,程序会崩溃
    // gob.Register([]interface{}{})

    input := map[string]interface{}{"X": []interface{}{1, 2}}
    buff := new(bytes.Buffer)
    enc := gob.NewEncoder(buff)
    if err := enc.Encode(input); err != nil {
    log.Panic("e1: ", err)
    }
    b1 := buff.Bytes()

    output := map[string]interface{}{}
    if err := gob.NewDecoder(bytes.NewReader(b1)).Decode(&output); err != nil {
    log.Panic("e2: ", err)
    }

    fmt.Println(output)
    }

MsgPack

  • MessagePack是一种高效的二进制序列化格式。它允许你在多种语言(如JSON)之间交换数据。但它更快更小。
  • 它和json存在类似的问题就是 序列化的时候会将空接口存放的 int都序列化成 int64 类型。

    安装

    1
    go get -u github.com/vmihailenco/msgpack

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
"log"

"github.com/vmihailenco/msgpack"
)

func main() {
test1 := map[string]interface{}{
"count": 1,
}

data1, err := msgpack.Marshal(test1)
if err != nil {
log.Fatal(err)
return
}

fmt.Println(data1)

test2 := make(map[string]interface{})

if err := msgpack.Unmarshal(data1, &test2); err != nil {
log.Fatal(err)
return
}

for _, v := range test2 {
fmt.Printf("value : %v, type = %T\n", v, v)
}
}
  • 输出结果如下:
    1
    2
    [129 165 99 111 117 110 116 211 0 0 0 0 0 0 0 1]
    value : 1, type = int64

总结

  • json序列化空接口存放的数字类型(整型、浮点型等)都序列化成float64类型。
  • msgpack序列化空接口存放的 int 都序列化成 int64 类型。
  • 从序列化之后的体积来看 msgpack < json < gob, 但是gob对于 空接口interface{} 的数据类型有比较好的复原
  • 使用建议:在不涉及interface{}的情况下,使用体积较小的,如果使用了interface{}并且对数据类型要求较高的情况下,推荐使用gob