Menu

JSON di Golang: Marshal, Unmarshal, dan Struct Tag

Cara meng-encode dan men-decode JSON di Go dengan encoding/json: Marshal dan Unmarshal, struct tag seperti omitempty dan omitzero, pretty print, decode ke map[string]any, menolak field yang tidak dikenal, dan streaming dengan Decoder.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Marshal dan Unmarshal

json.Marshal mengubah nilai Go menjadi byte JSON. json.Unmarshal mengisi nilai Go dari byte JSON. Struct tag menentukan nama field di JSON.

Email tidak muncul di output pertama karena omitempty dan string kosong. Unmarshal butuh pointer (&back); mengirim nilainya langsung akan mengembalikan InvalidUnmarshalError.

Struct tag

Sintaks tag-nya adalah json:"name,option,option".

TagEfek
json:"user_id"pakai user_id sebagai key
json:"email,omitempty"hilangkan saat bernilai false, 0, "", nil, atau slice atau map kosong
json:",omitzero" (Go 1.24)hilangkan saat nilainya adalah zero value, atau IsZero()-nya mengembalikan true
json:"-"jangan pernah encode atau decode field ini
json:"-,"pakai key literal -
json:"count,string"encode angka atau bool sebagai string JSON ("42")
tanpa tagkey-nya adalah nama field Go, UserID

Password tidak pernah keluar dari struct, balance diberi tanda kutip, dan deleted_at hilang sementara created_at mencetak waktu nol 0001-01-01T00:00:00Z. Perbedaan itulah alasan omitzero ditambahkan: omitempty tidak pernah bekerja untuk struct, dan sudah lama menjadi kejutan saat dipakai dengan time.Time. Di Go 1.23 dan sebelumnya, pakai *time.Time dengan omitempty untuk mendapatkan efek yang sama.

MarshalIndent(v, prefix, indent) menghasilkan output yang mudah dibaca. Pakai untuk file konfigurasi dan debugging; API biasanya mengirim JSON yang ringkas.

Hanya field yang di-export

encoding/json memakai reflection dan hanya bisa melihat field yang di-export. Ini bug JSON paling umum di Go:

type point struct {
	x, y int // lowercase: json.Marshal(point{1, 2}) gives {}
}

Tidak ada error, tidak ada peringatan, hanya {} saat encode dan field yang tetap bernilai nol saat decode. Ubah huruf awal field menjadi kapital dan tambahkan tag untuk key berhuruf kecil.

Cara decoding mencocokkan field

Saat melakukan unmarshal ke struct:

  • Key dicocokkan dengan nama tag, atau dengan nama field, tanpa membedakan huruf besar dan kecil. {"NAME": "x"} mengisi field bertag json:"name".
  • Key yang tidak punya field yang cocok diabaikan diam-diam.
  • Field yang tidak punya key yang cocok mempertahankan nilainya saat ini. Unmarshal tidak me-reset-nya, jadi decode ke struct yang sudah berisi data akan menggabungkan data ke dalamnya.
  • Tipe yang tidak cocok (string padahal struct-nya punya int) mengembalikan *json.UnmarshalTypeError, tapi field lainnya tetap diisi.

Untuk menolak key yang tidak terduga, misalnya di API yang ketat atau file konfigurasi yang berisi salah ketik, pakai Decoder dengan DisallowUnknownFields:

Struktur tidak diketahui: map[string]any

Saat kamu tidak tahu bentuk datanya sebelumnya, decode ke map[string]any atau any. Pemetaannya tetap:

JSONGo
objectmap[string]any
array[]any
stringstring
numberfloat64
true / falsebool
nullnil

Dua jebakan terlihat di output. Setiap angka bertipe float64, jadi m["stock"].(int) akan gagal. Dan integer di atas 2^53 kehilangan presisi sebagai float64: id 9007199254740993 kembali sebagai ...992. UseNumber menghindari keduanya.

Kalau kamu tahu sebagian strukturnya, decode bagian itu ke struct dan pakai json.RawMessage untuk sisanya. Tipe ini menyimpan byte mentah sebuah field supaya kamu bisa men-decode-nya nanti, setelah tahu tipenya.

Slice, map, pointer, dan nil

Nilai GoJSON
slice nil (var s []int)null
slice kosong ([]int{})[]
map nilnull
pointer nilnull
[]bytestring base64
map[string]Tobject, key-nya diurutkan
map[int]Tobject dengan key integer sebagai string

Perbedaan slice nil dan slice kosong penting untuk client API yang mengharapkan array. Inisialisasi dengan []T{} atau make([]T, 0) saat field-nya harus berupa [].

Pakai field pointer (*int, *bool) saat kamu perlu membedakan "tidak ada" dari "nol" pada input. Setelah unmarshal, pointer nil berarti key-nya tidak ada atau bernilai null; pointer ke 0 berarti client mengirim 0.

Stream: Encoder dan Decoder

json.Marshal dan Unmarshal bekerja pada seluruh byte slice sekaligus. Untuk io.Reader atau io.Writer (body HTTP, file, stdin), pakai json.NewDecoder dan json.NewEncoder. Decoder juga bisa membaca rangkaian nilai JSON satu per satu:

Encoder.Encode menulis baris baru setelah setiap nilai. Secara default, Marshal maupun Encoder meng-escape <, >, dan & menjadi \u003c, \u003e, dan \u0026, sehingga JSON-nya aman disisipkan ke HTML. SetEscapeHTML(false) mematikannya. Di HTTP handler, json.NewDecoder(r.Body).Decode(&v) dan json.NewEncoder(w).Encode(v) adalah pasangan yang biasa dipakai.

Encoding kustom

Sebuah tipe bisa mengatur JSON-nya sendiri dengan mengimplementasikan json.Marshaler dan json.Unmarshaler. Kasus yang umum adalah enum iota yang harus tampil sebagai nama, bukan angka:

MarshalJSON punya value receiver supaya bisa dipakai untuk nilai maupun pointer; UnmarshalJSON butuh pointer receiver karena ia mengubah nilainya. Tipe yang hanya butuh bentuk string bisa mengimplementasikan encoding.TextMarshaler (MarshalText) sebagai gantinya, yang juga membuatnya bisa dipakai sebagai key map.

Kesalahan umum

  • Nama field berhuruf kecil. Field itu dilewati diam-diam.
  • Mengirim nilai ke Unmarshal. Fungsi ini butuh pointer.
  • Mengabaikan error. JSON yang rusak dan tipe yang tidak cocok hanya dilaporkan lewat error itu.
  • Menganggap angka di map[string]any bertipe int. Angka itu bertipe float64.
  • Mengira omitempty akan menghilangkan struct kosong atau time.Time yang nol. Pakai omitzero di Go 1.24, atau pointer.
  • Mengandalkan urutan field di map. Key map diurutkan di output; field struct tetap mengikuti urutan deklarasinya.

Pertanyaan yang sering diajukan

Bagaimana cara mengubah struct menjadi JSON di Go?

Panggil json.Marshal(v), yang mengembalikan []byte dan sebuah error. Hanya field yang di-export (namanya diawali huruf kapital) yang disertakan. Pakai struct tag seperti json:"name" pada field untuk mengatur key JSON-nya. Untuk output yang berindentasi, pakai json.MarshalIndent(v, "", " ").

Kenapa field struct saya tidak muncul di output JSON?

encoding/json hanya bisa melihat field yang di-export. Field bernama name (huruf kecil) tidak terlihat olehnya, baik saat marshal maupun saat unmarshal. Ubah huruf awal field menjadi kapital dan atur key JSON-nya dengan tag seperti json:"name".

Apa fungsi omitempty di JSON Go?

omitempty menghilangkan field dari output saat isinya berupa nilai kosong: false, 0, "", pointer atau interface yang nil, atau slice atau map yang kosong. Opsi ini tidak menganggap struct atau time.Time sebagai kosong. Go 1.24 menambahkan omitzero, yang menghilangkan nilai apa pun yang merupakan zero value tipenya (atau yang method IsZero()-nya mengembalikan true), termasuk struct dan time.Time.

Bagaimana cara mem-parsing JSON yang strukturnya tidak diketahui di Go?

Lakukan unmarshal ke map[string]any (atau any). Object menjadi map[string]any, array menjadi []any, string menjadi string, boolean menjadi bool, dan setiap angka menjadi float64. Pakai type assertion untuk membaca nilainya, dan Decoder.UseNumber kalau integer besar harus tetap persis.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai