Go でオブジェクトをシリアライズ(シリアル化)したい
Go 言語(以下 Golang)で、オブジェクトを保存 & 読み込みできるようにシリアライズしたい。
JSON 文字列に変換してもいいのですが、json.Marshaler
や json.Unmarshaler
インターフェースを実装しないといけないものだと面倒です。
特に、ユーザーが保存データを直接編集する必要もないし、他のプログラム言語とのやりとりもない場合に、PHP の serialize
くらい簡単に保存可能な値に変換できないものか悩みます。
「"golang" phpのserializeみたいなもの」でググってもドンピシャの記事が出てこなかったので、自分のググラビリティとして。
TL; DR (今北産業)
- Golang には
gob
という自己表記型のデータ形式がある。(Go v1 から実装済み) -
encoding/gob
パッケージのEncode
でシリアル化、Decode
で復元できる。 - Golang 同士でしか使えないが、バイト・データなのでサイズは小さく済む。
- 参考文献
- Gobs of data | The Go Blog @ go.dev
- データのやりとりに gob を使う @ Qiita
- encoding/gob | Standard library @ pkg.go.dev
- Protocol Buffers - Google's data interchange format @ GitHub
-
Key-Value 型のデータを保存する際に、統一したインターフェースで、さまざまな保存先に保存・読み込みをしたい場合の便利なパッケージ。(
file
保存でJSON
やGob
を選択できる)- github.com/philippgille/gokv @ GitHub
TS; DR (マスター、動くものをくれ)
シンプル・バージョン
main.go
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type SimpleData struct {
ID int64
Message string
}
func main() {
dataOrig := SimpleData{ID: 123, Message: "my message"}
enc, err := EncodeToGob(dataOrig)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Enc: %x\n", enc)
dec, err := DecodeFromGob(enc)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Dec: %v\n", dec)
// Output:
// Enc: 2bff810301010a53696d706c654461746101ff820001020102494401040001074d657373616765010c00000012ff8201fff6010a6d79206d65737361676500
// Dec: &{123 my message}
}
func EncodeToGob(simpDat SimpleData) ([]byte, error) {
buf := bytes.NewBuffer(nil)
if err := gob.NewEncoder(buf).Encode(&simpDat); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func DecodeFromGob(gobData []byte) (*SimpleData, error) {
var simpDat SimpleData
buf := bytes.NewBuffer(gobData)
if err := gob.NewDecoder(buf).Decode(&simpDat); err != nil {
return nil, err
}
return &simpDat, nil
}
- オンラインで動作を見る @ GoPlayground
いささか複雑バージョン
main.go
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
func main() {
// 保存したいオブジェクトの作成
apple1 := Fruit{
Name: "apple",
Color: Red,
}
fmt.Println(apple1)
// オブジェクトを保存可能なバイト・データに変換
buf := bytes.NewBuffer(nil)
err := gob.NewEncoder(buf).Encode(&apple1)
ExitOnError(err)
dataToSave := buf.Bytes() // 保存するバイト・データ
fmt.Println("Saved data:", dataToSave)
// 保存されたバイトデータからオブジェクトを復元
var apple2 Fruit // 復元先の空のオブジェクト
buf = bytes.NewBuffer(dataToSave)
err = gob.NewDecoder(buf).Decode(&apple2)
ExitOnError(err)
fmt.Println(apple2)
// Output:
// Before: I am apple. My color is red.
// Saved data: [38 255 129 3 1 1 5 70 114 117 105 116 1 255 130 0 1 2 1 4 78 97 109 101 1 12 0 1 5 67 111 108 111 114 1 4 0 0 0 12 255 130 1 5 97 112 112 108 101 1 2 0]
// After: I am apple. My color is red.
}
// ----------------------------------------------------------------------------
// Type: Color
// ----------------------------------------------------------------------------
// Color は色情報を保持する enum 用の型です。
type Color int
const (
Unknown Color = iota // Unknown は不明な色を表す enum 定数です。
Red // Red は赤色を表す enum 定数です。
Green // Green は緑色を表す enum 定数です。
Yellow // Yellow は黄色を表す enum 定数です。
)
// String は fmt.Stringer インターフェースの実装です。自身の色を示す文字列を返します。
func (c Color) String() string {
switch c {
case Red:
return "red"
case Green:
return "green"
case Yellow:
return "yellow"
}
return "unknown"
}
// ----------------------------------------------------------------------------
// Type: Fruit
// ----------------------------------------------------------------------------
// Fruit はフルーツの情報を保持する型です。
type Fruit struct {
Name string
Color Color
}
// String は fmt.Stringer インターフェースの実装です。自身を示す文字列を返します。
func (f Fruit) String() string {
return fmt.Sprintf("I am %s. My color is %s.", f.Name, f.Color)
}
// ----------------------------------------------------------------------------
// Functions
// ----------------------------------------------------------------------------
// ExitOnError は err が nil でない場合に、エラーで終了します。
func ExitOnError(err error) {
if err != nil {
log.Fatal(err)
}
}
- オンラインで動作をみる @ GoPlayground