JSON dan CSV mudah dibaca mata, tetapi teks tersebut boros ukuran dan lambat diurai ketika kedua ujung pertukaran data sama-sama program Go. Package encoding/gob menghadirkan format biner khusus Go: encoder mengirim nilai lengkap dengan deskripsi tipenya, decoder memeriksa kecocokan tipe, lalu menyalin nilai ke variabel tujuan. Pada chapter ini kita akan melakukan round-trip struct lewat bytes.Buffer, membuktikan field unexported tidak ikut, dan menangkap galat ketika tipe sumber serta tujuan tidak cocok.
Kapan gob, Kapan JSON #
gob bukan pengganti JSON. Format gob hanya dipahami program Go, sehingga tidak cocok untuk API publik. Kekuatan gob muncul pada kanal internal antar komponen Go: cache lokal di berkas atau Redis, session store, antrian pesan internal, hingga dasar package net/rpc. Tabel berikut merangkum perbedaan keduanya:
| Aspek | JSON | gob |
|---|---|---|
| Bentuk data | Teks, bisa dibaca mata | Biner, tidak untuk dibaca |
| Konsumen | Bahasa apa pun | Hanya program Go |
| Cocok tipe | Longgar, berbasis nama field | Ketat, nama dan tipe field harus cocok |
| Kecepatan dan ukuran | Lebih lambat, lebih besar | Lebih cepat, lebih ringkas |
| Kasus pakai | API publik, konfigurasi | Cache, RPC, antrian internal |
Round-Trip di Atas bytes.Buffer #
Alur gob selalu tiga aktor: satu io.Writer atau io.Reader, satu gob.NewEncoder/gob.NewDecoder di atasnya, dan nilai yang dikirim lewat Encode atau diterima lewat Decode. Program gob-demo.go berikut memakai bytes.Buffer sebagai perantara — pola yang sama berlaku untuk os.File, koneksi jaringan, atau bytes.Buffer di balik net/rpc:
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Pemesanan struct {
ID string
Item string
Jumlah int
}
type Tagihan struct {
ID string
Item string
Jumlah string // sengaja beda tipe dari Pemesanan.Jumlah
}
type Pengguna struct {
Nama string
nik string // unexported: tidak pernah ikut ke gob
}
func main() {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(Pemesanan{ID: "P-001", Item: "Buku Go", Jumlah: 2}); err != nil {
fmt.Println("encode gagal:", err)
return
}
fmt.Printf("ukuran data biner: %d byte\n", buf.Len())
var hasil Pemesanan
if err := gob.NewDecoder(&buf).Decode(&hasil); err != nil {
fmt.Println("decode gagal:", err)
return
}
fmt.Printf("hasil decode: %+v\n", hasil)
var bufP bytes.Buffer
if err := gob.NewEncoder(&bufP).Encode(Pengguna{Nama: "Siti", nik: "3201010101010001"}); err != nil {
fmt.Println("encode gagal:", err)
return
}
var p2 Pengguna
if err := gob.NewDecoder(&bufP).Decode(&p2); err != nil {
fmt.Println("decode gagal:", err)
return
}
fmt.Printf("pengguna: %+v\n", p2)
var bufT bytes.Buffer
if err := gob.NewEncoder(&bufT).Encode(Pemesanan{ID: "P-002", Item: "Kopi", Jumlah: 1}); err != nil {
fmt.Println("encode gagal:", err)
return
}
var t Tagihan
err := gob.NewDecoder(&bufT).Decode(&t)
fmt.Println("galat tipe beda:", err)
}Output yang Dihasilkan:
ukuran data biner: 72 byte
hasil decode: {ID:P-001 Item:Buku Go Jumlah:2}
pengguna: {Nama:Siti nik:}
galat tipe beda: gob: wrong type (string) for received field Pemesanan.JumlahMari kita telaah baris demi baris program di atas:
- Baris 9-13 (type Pemesanan): struct sumber dengan tiga field exported yang akan dikirim ke stream gob.
- Baris 15-19 (type Tagihan): struct tujuan yang sengaja membedakan tipe field
Jumlahsupaya galat tipe bisa dipancing. - Baris 27-32:
gob.NewEncoder(&buf).Encode(...)mengirim nilaiPemesananke buffer. Baris 32 mencetak ukuran hasil biner — kita sengaja tidak mencetak isi byte mentahnya karena bentuk biner tidak dimaksudkan untuk dibaca manusia. - Baris 34-39:
gob.NewDecoder(&buf).Decode(&hasil)membaca kembali isi buffer ke struct tujuan;Decodememerlukan pointer agar bisa mengisi variabel. - Baris 41-51: percobaan kedua memakai struct
Penggunayang punya fieldnikunexported. Hasil decode menunjukkannikkosong: gob hanya mengirim field exported, sama seperti JSON dan XML. - Baris 53-60: percobaan ketiga mengirim
Pemesananlalu mendecode ke structTagihanyang fieldJumlah-nya bertipestring. Karena nama field sama tetapi tipe berbeda, decoder menolak dengan galatgob: wrong type (string) for received field Pemesanan.Jumlah.
Ketatnya Pemeriksaan Tipe #
Galat pada percobaan ketiga adalah perilaku inti gob: decoder mencocokkan setiap field yang dikirim dengan field bernama sama pada tipe tujuan, dan tipe keduanya harus persis sama. Field yang hanya ada di satu sisi tidak menimbulkan galat — field sumber yang tak dikenal diabaikan, field tujuan yang kosong dibiarkan bernilai zero. Pemeriksaan ketat inilah yang membuat gob aman untuk pertukaran internal: perubahan tipe yang tidak disengaja langsung ketahuan saat decode, bukan diam-diam menghasilkan data salah.
Nilai Interface Perlu gob.Register #
Encode dan Decode bekerja otomatis untuk tipe konkret. Ketika yang dikirim adalah nilai interface, misalnya error atau field any, decoder tidak tahu tipe konkret yang disembunyikan di balik interface tersebut. Solusinya: panggil gob.Register(nilai konkret) sekali di awal program agar encoder boleh mengirim nama tipenya dan decoder tahu cara membangunnya kembali. Panggilan Register biasanya diletakkan di init() atau awal main().
Referensi #
- Dokumentasi package encoding/gob — referensi resmi aturan encode, decode, dan Register.
- Dokumentasi package encoding — daftar format encoding pada standard library.
gob memetakan struct apa adanya. Ketika kita ingin bentuk keluaran yang sepenuhnya dikendalikan — format tanggal tetap atau uang dalam satuan sen — JSON menyediakan pintu MarshalJSON yang kita bahas pada chapter Custom Marshaler berikutnya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
gob-demo.go
Unduh berkaspackage main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Pemesanan struct {
ID string
Item string
Jumlah int
}
type Tagihan struct {
ID string
Item string
Jumlah string // sengaja beda tipe dari Pemesanan.Jumlah
}
type Pengguna struct {
Nama string
nik string // unexported: tidak pernah ikut ke gob
}
func main() {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(Pemesanan{ID: "P-001", Item: "Buku Go", Jumlah: 2}); err != nil {
fmt.Println("encode gagal:", err)
return
}
fmt.Printf("ukuran data biner: %d byte\n", buf.Len())
var hasil Pemesanan
if err := gob.NewDecoder(&buf).Decode(&hasil); err != nil {
fmt.Println("decode gagal:", err)
return
}
fmt.Printf("hasil decode: %+v\n", hasil)
var bufP bytes.Buffer
if err := gob.NewEncoder(&bufP).Encode(Pengguna{Nama: "Siti", nik: "3201010101010001"}); err != nil {
fmt.Println("encode gagal:", err)
return
}
var p2 Pengguna
if err := gob.NewDecoder(&bufP).Decode(&p2); err != nil {
fmt.Println("decode gagal:", err)
return
}
fmt.Printf("pengguna: %+v\n", p2)
var bufT bytes.Buffer
if err := gob.NewEncoder(&bufT).Encode(Pemesanan{ID: "P-002", Item: "Kopi", Jumlah: 1}); err != nil {
fmt.Println("encode gagal:", err)
return
}
var t Tagihan
err := gob.NewDecoder(&bufT).Decode(&t)
fmt.Println("galat tipe beda:", err)
}