LoginSignup
0
0

【Golang】PHP の serialize のようにオブジェクトを保存可能なデータにしたい【gob】

Last updated at Posted at 2022-06-08

Go でオブジェクトをシリアライズ(シリアル化)したい

Go 言語(以下 Golang)で、オブジェクトを保存 & 読み込みできるようにシリアライズしたい。

JSON 文字列に変換してもいいのですが、json.Marshalerjson.Unmarshaler インターフェースを実装しないといけないものだと面倒です。

特に、ユーザーが保存データを直接編集する必要もないし、他のプログラム言語とのやりとりもない場合に、PHP の serialize くらい簡単に保存可能な値に変換できないものか悩みます。

「"golang" phpのserializeみたいなもの」でググってもドンピシャの記事が出てこなかったので、自分のググラビリティとして。

TL; DR (今北産業)

  1. Golang には gob という自己表記型のデータ形式がある。(Go v1 から実装済み)
  2. encoding/gob パッケージの Encode でシリアル化、Decode で復元できる。
  3. Golang 同士でしか使えないが、バイト・データなのでサイズは小さく済む。

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
}

いささか複雑バージョン

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0