【Go / Python】MessagePack の Encode/Decode を複数の言語で試す

仕事でバイナリフォーマットを扱う機会がありそうなので MessagePack の Encode/Decode を Go, Python, Scala で試してみました。

  • macOS: 10.12.1
  • Go: 1.7
  • Python: 2.7.12
  • Scala: 2.12.0

データの準備

以下の JSON を msgpack-cli で MessagePack にシリアライズする。

$ go get github.com/jakm/msgpack-cli

$ cat test.json | jq
{
  "timestamp": 1478743555,
  "user": "bob",
  "age": 28
}

$ msgpack-cli encode test.json --out test.bin
$ msgpack-cli decode test.bin --pp
{
  "age": 28,
  "timestamp": 1478743555,
  "user": "bob"
}

サイズ比較と hexdump で 16進数 と ASCII を出力して中身を確認してシリアライズされていることを確認。

$ ls -lh test.*
-rw-r--r--  1 user  staff    30B 12  4 00:35 test.bin
-rw-r--r--  1 user  staff    52B 12  4 00:35 test.json

$ hexdump -C test.json
00000000  7b 22 74 69 6d 65 73 74  61 6d 70 22 3a 20 31 34  |{"timestamp": 14|
00000010  37 38 37 34 33 35 35 35  2c 20 22 75 73 65 72 22  |78743555, "user"|
00000020  3a 20 22 62 6f 62 22 2c  20 22 61 67 65 22 3a 20  |: "bob", "age": |
00000030  32 38 7d 0a                                       |28}.|
00000034

$ hexdump -C test.bin
00000000  83 a9 74 69 6d 65 73 74  61 6d 70 ce 58 23 d6 03  |..timestamp.X#..|
00000010  a4 75 73 65 72 a3 62 6f  62 a3 61 67 65 1c        |.user.bob.age.|
0000001e

Go

Go では ugorji/go を使ってみる。

$ go get github.com/ugorji/go/codec
package main

import (
	"github.com/ugorji/go/codec"
	"io/ioutil"
	"log"
)

var (
	v  interface{} // value to decode/encode into
	b  []byte
	mh codec.MsgpackHandle
)

func DecodeMessagePack(buf []byte) error {
	err := codec.NewDecoderBytes(buf, &mh).Decode(&v)
	if err != nil {
		return err
	}
	log.Printf("Decoded: %#v", v)

	return err
}

func main() {
	b, err := ioutil.ReadFile("../data/test.bin")
	if err != nil {
		log.Println(err)
	}
	log.Printf("MessagePack: %#v", b)

	err = DecodeMessagePack(b)
	if err != nil {
		log.Println(err)
	}
}
$ go run ugori_example.go
2016/12/04 00:39:34 MessagePack: []byte{0x83, 0xa9, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0xce, 0x58, 0x23, 0xd6, 0x3, 0xa4, 0x75, 0x73, 0x65, 0x72, 0xa3, 0x62, 0x6f, 0x62, 0xa3, 0x61, 0x67, 0x65, 0x1c}
2016/12/04 00:39:34 Decoded: map[interface {}]interface {}{"timestamp":0x5823d603, "user":[]uint8{0x62, 0x6f, 0x62}, "age":28}

Python

Python では msgpack-python を使ってみる。

$ pip install msgpack-python
# -*- coding: utf-8 -*-

import msgpack
from io import BytesIO

buf = BytesIO()

with open("../data/test.bin", "rb") as f:
    buf.write(f.read())

buf.seek(0)

unpacker = msgpack.Unpacker(buf)
for unpacked in unpacker:
    print(unpacked)
$ python msgpack_example.py
{'timestamp': 1478743555, 'age': 28, 'user': 'bob'}

msgpack-python 以外の候補として u-msgpack-python もある。

Scala

Scalaでは msgpack-scala を使ってみる。

build.sbt に以下を追加する。

libraryDependencies += "org.msgpack" %% "msgpack-scala" % "0.6.11"

libraryDependencies += "org.slf4j" % "slf4j-log4j12" % "1.7.10"

Scala(Java)力が足りなくて, test.bin から Decode が上手くできなかったのが悔やまれる。

import org.msgpack.annotation.Message
import org.msgpack.ScalaMessagePack

@Message // Don't forget to add Message annotation.
class Test{
  var timestamp : Int = 0
  var user : String = ""
  var age : Int = 0
}

object MessagePack{
   def main(args: Array[String]){
      val obj = new Test()
      obj.timestamp = 1478743555
      obj.user = "bob"
      obj.age = 28

      val serialized : Array[Byte] = ScalaMessagePack.write(obj)
      println(serialized)
      val deserialized : Test = ScalaMessagePack.read[Test](serialized)
      println(deserialized)
   }
}

Scalaでは msgpack4z も良さそう。


[1] Go の msgpack ライブラリ比較
[2] ドワンゴオリジナルのScala研修資料
[3] java.lang.ClassNotFoundException: org.slf4j.LoggerFactory in Intellij, scala project with playframework
[4] Scala入門時に役立つ情報まとめ