Paket encoding/json mengubah nilai Go menjadi dokumen JSON dan sebaliknya, sebuah proses yang disebut marshal untuk penyandian dan unmarshal untuk pembacaan kembali. Topik ini membahas penyandian struct beserta tag JSON-nya, pembacaan dokumen JSON ke struct, dan dekoder aliran yang memproses dokumen JSON satu per satu tanpa memuat semuanya sekaligus. Contohnya memakai catatan gudang berisi judul, status, dan nomor urut, dengan keluaran yang tetap karena urutan field struct menentukan urutan kunci JSON. Setelah membacanya, pertukaran data JSON dengan program lain dapat dilakukan dengan tipe yang benar dan penanganan error yang lengkap.
Struct, tag, dan marshal#
Fungsi json.Marshal menyandikan nilai Go menjadi []byte berisi JSON, sedangkan json.MarshalIndent menyandikannya dengan indentasi agar mudah dibaca. Nama kunci JSON mengikuti nama field struct secara default, tetapi nama itu biasanya diubah melalui tag, yaitu anotasi bertanda kutip belakang yang ditempel pada field dan dituliskan dalam bentuk json:"nama". Tag juga dipakai untuk melewatkan field dengan tanda -. Urutan kunci pada hasil mengikuti urutan field pada struct, sehingga keluaran program yang sama selalu identik. Tipe map juga dapat disandikan, dan kuncinya diurutkan secara alfabetis agar hasilnya tetap dapat diprediksi. Program berikut menyandikan satu struct catatan dan satu peta penghitung.
package main
import (
"encoding/json"
"fmt"
)
type Catatan struct {
Judul string `json:"judul"`
Status string `json:"status"`
Urut int `json:"urut"`
}
func main() {
c := Catatan{Judul: "Laporan Gudang", Status: "draf", Urut: 7}
b, err := json.MarshalIndent(c, "", " ")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(string(b))
penghitung := map[string]int{"cabang-c": 3, "cabang-a": 1, "cabang-b": 2}
m, err := json.Marshal(penghitung)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(string(m))
}{
"judul": "Laporan Gudang",
"status": "draf",
"urut": 7
}
{"cabang-a":1,"cabang-b":2,"cabang-c":3}Keluaran di atas memperlihatkan dua sifat yang saling melengkapi. Struct Catatan disandikan sesuai urutan fieldnya, yaitu judul, status, lalu nomor urut, sehingga hasilnya mudah dibandingkan antarversi program. Peta penghitung disandikan dengan kunci yang diurutkan dari cabang-a sampai cabang-c, meskipun deklarasinya menyebut urutan lain, karena peta di Go tidak memiliki urutan tersendiri. Tag "judul" dan seterusnya membuat nama kunci JSON ditulis dengan huruf kecil semua, mengikuti kebiasaan format JSON pada umumnya. Opsi tambahan dapat dilekatkan setelah nama kunci dengan pemisah koma, misalnya json:"status,omitempty" yang menghilangkan field tersebut dari hasil ketika nilainya nol.
Unmarshal JSON ke struct#
Fungsi json.Unmarshal membaca []byte berisi JSON dan mengisinya ke pointer struct yang diberikan. Pencocokan dilakukan berdasarkan nama kunci JSON terhadap tag field, tanpa memedulikan urutannya, dan field yang tidak ada pada dokumen dibiarkan bernilai nol. Error dikembalikan apabila dokumen bukan JSON yang sah atau tipe data tidak cocok dengan field tujuan, misalnya teks yang diisikan ke field int. Program berikut membaca satu dokumen lengkap dan satu dokumen yang hanya memuat sebagian fieldnya.
package main
import (
"encoding/json"
"fmt"
)
type Catatan struct {
Judul string `json:"judul"`
Status string `json:"status"`
Urut int `json:"urut"`
}
func main() {
dokumen := `{"judul":"Laporan Gudang","status":"terkirim","urut":7}`
var c Catatan
if err := json.Unmarshal([]byte(dokumen), &c); err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(c.Judul, c.Status, c.Urut)
sebagian := `{"judul":"Catatan Singkat"}`
var k Catatan
if err := json.Unmarshal([]byte(sebagian), &k); err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("%q %q %d\n", k.Judul, k.Status, k.Urut)
}Laporan Gudang terkirim 7
"Catatan Singkat" "" 0Dokumen kedua hanya memuat kunci judul, sehingga field Status dan Urut tetap berupa nilai nol bertipe masing-masing, yaitu string kosong dan angka 0. Perilaku ini menegaskan bahwa Unmarshal tidak menuntut dokumen yang lengkap, tetapi juga tidak memberi tahu field mana yang hilang, sehingga field yang wajib ada harus diperiksa sendiri setelah pembacaan selesai.
Dekoder streaming dengan json.Decoder#
Untuk aliran dokumen JSON yang berkelanjutan, misalnya satu dokumen per baris dari log atau koneksi jaringan, json.Decoder membaca satu dokumen pada satu waktu melalui metode Decode. Pendekatan ini tidak menunggu seluruh data selesai, sehingga dokumen pertama dapat diproses ketika dokumen berikutnya masih dalam perjalanan. Metode More melaporkan apakah masih ada dokumen lain pada aliran tersebut. Program berikut memproses dua catatan yang ditulis berurutan pada satu aliran teks.
package main
import (
"encoding/json"
"fmt"
"strings"
)
type Catatan struct {
Judul string `json:"judul"`
Urut int `json:"urut"`
}
func main() {
aliran := strings.NewReader(
`{"judul":"Draf laporan","urut":1}` + "\n" +
`{"judul":"Laporan final","urut":2}` + "\n")
dek := json.NewDecoder(aliran)
for dek.More() {
var c Catatan
if err := dek.Decode(&c); err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("urut %d: %s\n", c.Urut, c.Judul)
}
}urut 1: Draf laporan
urut 2: Laporan finalFungsi json.NewDecoder menerima sembarang io.Reader, sehingga sumber alirannya dapat berupa berkas, koneksi jaringan, atau string pada contoh ini. Perulangan berhenti ketika More melaporkan aliran habis, dan setiap Decode mengembalikan error yang wajib diperiksa. Pola ini menjadi dasar pemrosesan log terstruktur dan respons bertahap dari server. Apabila aliran berupa satu array JSON tunggal, metode Token pada dekoder dapat melewati tanda kurung array sebelum dokumen-dokumen di dalamnya dibaca satu per satu. Pendekatan yang sama berlaku untuk respons server yang dikirim secara bertahap.
Ketiga program dapat dijalankan dari terminal sebagaimana berikut.
# siapkan folder kerja untuk topik ini
mkdir -p /tmp/gopustaka/json
cp catatan-json.go baca-json.go dekoder-aliran.go /tmp/gopustaka/json/
cd /tmp/gopustaka/json
# jalankan program penyandian JSON
go run catatan-json.go
# jalankan program pembacaan JSON
go run baca-json.go
# jalankan program dekoder aliran
go run dekoder-aliran.go{
"judul": "Laporan Gudang",
"status": "draf",
"urut": 7
}
{"cabang-a":1,"cabang-b":2,"cabang-c":3}
Laporan Gudang terkirim 7
"Catatan Singkat" "" 0
urut 1: Draf laporan
urut 2: Laporan finalKesalahan umum#
- Melewatkan field privat pada struct yang hendak disandikan. Field berawalan huruf kecil diabaikan oleh
MarshaldanUnmarshaltanpa pesan error, sehingga datanya tidak pernah sampai. Perbaikannya, ekspor field tersebut atau salin isinya ke struct yang fieldnya diekspor. - Menulis tag tanpa tanda kutip belakang. Tag ditulis sebagai
json:"judul"di dalam kutip belakang, dan bentuk sepertijson="judul"tidak terbaca sebagai tag JSON. Perbaikannya, gunakan tanda kutip belakang dan awalanjson:. - Mengabaikan error pada
Unmarshal. Dokumen yang rusak menghasilkan struct dengan nilai nol, sehingga program berlanjut dengan data kosong. Perbaikannya, periksa error sebelum hasilnya dipakai. - Menganggap hasil unmarshal selalu lengkap. Field yang tidak ada pada dokumen dibiarkan bernilai nol tanpa pemberitahuan, sehingga data yang wajib ada dapat lolos begitu saja. Perbaikannya, periksa field wajib setelah pembacaan selesai sebelum nilainya dipakai.
- Memakai
Unmarshaluntuk aliran panjang. Fungsi tersebut menuntut seluruh dokumen berada di memori sebagai[]byte. Perbaikannya, gunakanjson.Decoderyang membaca dariio.Readersecara bertahap.
Ringkasan#
json.Marshaldanjson.MarshalIndentmenyandikan struct sesuai urutan fieldnya, sedangkan peta disandikan dengan kunci yang diurutkan.- Tag
json:"nama"mengganti nama kunci JSON dan hanya berlaku pada field yang diekspor. json.Unmarshalmengisi struct dari dokumen JSON berdasarkan nama kunci, dengan field yang tidak ada dibiarkan bernilai nol.json.Decodermemproses aliran dokumen JSON satu per satu melaluiDecodedanMore, sehingga cocok untuk data yang berkelanjutan.
Referensi#
- Package encoding/json — The Go Authors — Dokumentasi resmi Marshal, Unmarshal, Decoder, dan aturan tag JSON.
- JSON and Go — The Go Blog — Artikel resmi yang memperkenalkan Marshal dan Unmarshal beserta tag-nya.
- The Go Documentation — Halaman induk dokumentasi resmi Go yang memuat panduan pustaka standar.