Jika kamu pernah bekerja dengan microservices, pasti sudah tidak asing lagi dengan Protobuf sebagai serialization format. Dukungan Protobuf terhadap message exchange yang cepat sekaligus schema-typed menjadi alasan utama format ini digandrungi di ekosistem Go dan berbagai bahasa lainnya. Namun, realita di lapangan sering kali menuntut interoperabilitas service-service berbasis JSON ― baik untuk logging, REST API, ataupun debugging. Maka di sinilah fitur marshal dan unmarshal JSON pada Protobuf menjadi sangat penting.
Pada artikel kali ini, kita akan membahas secara komprehensif proses Marshal dan Unmarshal JSON pada Protobuf di Go. Kita akan mengeksplorasi implementasi, simulasi, hingga potensi pitfall yang mesti diwaspadai.
Ringkasan Istilah
| Istilah | Penjelasan | 
|---|---|
| Marshal | Mengkonversi (encode) data struktur ke format lain (misal: Protobuf ke JSON) | 
| Unmarshal | Mengkonversi (decode) data dari format lain ke struktur (misal: JSON ke Protobuf struct) | 
| Protobuf | Format serialisasi biner yang schema-typed dari Google | 
| JSON | Format data berbasis teks yang mudah dibaca manusia | 
Kenapa Protobuf JSON Marshal/Unmarshal Diperlukan?
Walau Protobuf mengandalkan encoding binary, namun berbagai use-case meminta format JSON. Beberapa situasi seperti:
- Logging berbasis JSON untuk konsistensi stack.
- REST API yang secara kontrak memerlukan body berformat JSON.
- Debugging manual terhadap payload.
- Backward compatibility dengan sistem warisan yang masih pakai JSON.
Diagram Alur Proses
Mari kita ilustrasikan alurnya memakai diagram flowchart sederhana.
flowchart LR
    A[Struct Protobuf] --Marshal--> B[JSON]
    B --Unmarshal--> C[Struct Protobuf]
Alur di atas memperjelas bahwa ada proses dua arah dari Protobuf ke JSON (Marshal) dan sebaliknya (Unmarshal).
Setup Protobuf dan Go
Sebelum masuk ke kode, pastikan punya setup berikut:
- Golang (1.18+)
- protoc
- protobuf-go plugin: google.golang.org/protobuf
Contoh .proto file sederhana:
syntax = "proto3";
package article;
message User {
    int32 id = 1;
    string name = 2;
    bool is_active = 3;
}
Generate Go file:
protoc --go_out=. user.proto
Implementasi Marshal dan Unmarshal
1. Marshal ke JSON
google.golang.org/protobuf/encoding/protojson menyediakan utility untuk Marshal dan Unmarshal.
Contoh kode:
package main
import (
    "fmt"
    "log"
    "google.golang.org/protobuf/encoding/protojson"
    pb "path/to/generated/package/article"
)
func main() {
    user := &pb.User{
        Id:       50,
        Name:     "Sophia",
        IsActive: true,
    }
    // Marshal ke JSON
    data, err := protojson.Marshal(user)
    if err != nil {
        log.Fatalf("marshal error: %v", err)
    }
    fmt.Println(string(data))
}
Output:
{"id":50,"name":"Sophia","isActive":true}
Pro Tip:
Output secara default hanya membawa field yang diisi. Untuk output yang lebih readable, gunakan:
marshaller := protojson.MarshalOptions{
    Multiline:       true,
    EmitUnpopulated: true,
    Indent:          "  ",
}
jsonData, _ := marshaller.Marshal(user)
fmt.Println(string(jsonData))
2. Unmarshal dari JSON ke Protobuf
Mari simulasikan proses sebaliknya: JSON ke struct Protobuf.
jsonStr := `{"id":90,"name":"Aston","isActive":false}`
var user pb.User
err := protojson.Unmarshal([]byte(jsonStr), &user)
if err != nil {
    log.Fatalf("unmarshal error: %v", err)
}
fmt.Printf("Unmarshalled: %#v\n", user)
Output:
Unmarshalled: article.User{Id:90, Name:"Aston", IsActive:false, ...}
Simulasi: Menangani Field Kosong
Berikut adalah tabel mapping perilaku marshal/unmarshal terkait field kosong pada Protobuf dan JSON.
| Field Protobuf | Nilai | Setelah Marshal ke JSON | Uraian | 
|---|---|---|---|
| id (int32) | 0 | Hilang/Tampil (1) | Default hilang, atau tampil jika EmitUnpopulated | 
| name (string) | "" | Hilang/Tampil (1) | Sama | 
| is_active (bool) | false | Hilang/Tampil (1) | Sama | 
(1) Mengatur EmitUnpopulated: true pada opsi Marshal membuat JSON mencantumkan default value.
Contoh Perbandingan:
user := &pb.User{} // semuanya default
defaultOutput, _ := protojson.Marshal(user)
withOpt := protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(user)
fmt.Println("Biasa:", string(defaultOutput))
fmt.Println("EmitUnpopulated:", string(withOpt))
Output:
Biasa: {}
EmitUnpopulated: {"id":0,"name":"","isActive":false}
Pitfall dan Best Practices
1. Ekspose Internal Field
- Field Protobuf tidak semua langsung tampil di JSON, misal field oneof atau map.
2. Nilai Default
- Nilai default di Protobuf (0,false,"") tidak otomatis muncul di JSON tanpa setEmitUnpopulated.
3. Kompatibilitas
- JSON Protobuf bisa beda dengan JSON tag struct Go biasa. Misal, field-casing dan enum bisa beda penulisan.
4. Array dan Repeated Field
- Empty list tetap muncul sebagai []jikaEmitUnpopulated: true.
5. Oneof / Any
- Pemrosesan oneof dan type Anymembutuhkan perhatian khusus. Kadang-kadang lebih baik membuat handler custom.
Skenario Lanjutan: Interop Service
Misalkan salah satu service mengirim JSON ke service lain, lalu dilakukan unmarshal ke Protobuf.
sequenceDiagram
    participant Client
    participant ServiceA
    participant ServiceB
    Client->>ServiceA: Kirim JSON Payload
    ServiceA->>ServiceA: Unmarshal ke Protobuf Struct
    ServiceA->>ServiceB: Marshal ke Binary Protobuf
    ServiceB->>ServiceB: Unmarshal dari Binary
Kesimpulan
Marshal dan Unmarshal antara Protobuf dan JSON di Go sangat mudah dengan bantuan protojson. Namun, seperti layaknya engineer yang teliti, penting untuk memahami default behavior, pengaturan option, serta antagonis kecil seperti field kosong atau distorsi serialisasi.
Bekerjalah dengan schema-first mindset dan pastikan output telah tervalidasi dengan konsumen dari masing-masing format. Pengalaman akan mengajarkan edge case yang tidak selalu ter-cover oleh tutorial standar.
Jika kamu ingin berdiskusi lebih jauh terkait schema evolution dan serialisasi di sistem enterprise, jangan ragu untuk berdiskusi di kolom komentar. Selamat berkarya dan happy marshalling! 🚀
Referensi
27 Menulis Skema Mutation di GraphQL
Artikel Terhangat
111 Menambahkan Mutation ke Skema gqlgen
10 Oct 2025
108 Menangani Resolver Otomatis dan Manual
10 Oct 2025
106 Menulis Skema `.graphqls` untuk gqlgen
10 Oct 2025

111 Menambahkan Mutation ke Skema gqlgen

108 Menangani Resolver Otomatis dan Manual

