Perbandingan Go 1.25 encoding/json v1 vs v2
encoding/json
package v2 dari Go merupakan implementasi baru yang bertujuan untuk memperbaiki beberapa kekurangan dari v1 yang ada (kurangnya konsistensi, perilaku mengejutkan, masalah kinerja). Ini adalah fitur eksperimental yang diaktifkan melalui tag build goexperiment.jsonv2
.
Poin terpenting adalah ketika v2 diaktifkan, v1 akan beroperasi sebagai lapisan kompatibilitas yang mengemulasi perilaku v1 di atas implementasi v2. Hal ini dicapai melalui fungsi DefaultOptionsV1()
di berkas v2_v1.go
. Artinya, v2 menyediakan opsi yang dapat mereplikasi perilaku v1 secara sempurna, sekaligus memperkenalkan perilaku default baru yang lebih ketat dan dapat diprediksi.
Tujuan utama v2 adalah sebagai berikut:
- Peningkatan Akurasi dan Prediktabilitas: Secara default menerapkan aturan yang lebih ketat (misalnya, peka huruf besar/kecil, melarang kunci duplikat) untuk mengurangi perilaku yang tidak terduga.
- Peningkatan Kinerja: Merancang ulang mesin parsing dan encoding untuk meningkatkan efisiensi.
- Peningkatan Fleksibilitas dan Kontrol: Memperkenalkan sistem opsi yang terperinci untuk memungkinkan pengembang mengontrol pemrosesan JSON secara tepat.
Perbedaan Utama dalam Makna/Perilaku
Perbedaan perilaku antara v1 dan v2 telah dirangkum berdasarkan item, dengan fokus pada berkas v2_test.go
.
1. Pencocokan Nama Bidang (Peka Huruf Besar/Kecil)
- Perilaku v1: Ketika unmarshaling anggota objek JSON ke bidang struktur Go, pencocokan dilakukan tidak peka huruf besar/kecil (case-insensitive). Baik
"FirstName"
maupun"firstname"
akan dipetakan ke bidangFirstName
. - Perilaku v2: Secara default, pencocokan dilakukan peka huruf besar/kecil (case-sensitive), hanya memetakan bidang yang cocok persis.
- Alasan Perubahan: Pencocokan yang tidak peka huruf besar/kecil dapat menjadi sumber perilaku yang tidak terduga dan menyebabkan penurunan kinerja saat memproses bidang yang tidak cocok. v2 mengadopsi perilaku yang lebih jelas dan dapat diprediksi sebagai standar.
- Opsi Terkait: Dalam v2, Anda dapat secara eksplisit mengaktifkan pencocokan tidak peka huruf besar/kecil per bidang menggunakan opsi tag
json:"...,case:ignore"
, atau menerapkan opsijson.MatchCaseInsensitiveNames(true)
secara global.
2. Perubahan Makna Opsi Tag omitempty
- Perilaku v1: Menghilangkan bidang berdasarkan "keadaan kosong" dari nilai Go. "Keadaan kosong" di sini berarti
false
,0
, pointer/antarmukanil
, array/slice/map/string dengan panjang 0. - Perilaku v2: Menghilangkan bidang berdasarkan "keadaan kosong" dari nilai JSON yang di-encode. Artinya, akan dihilangkan jika di-encode sebagai
null
,""
,{}
,[]
. - Alasan Perubahan: Definisi v1 tergantung pada sistem tipe Go. v2 menyediakan perilaku yang lebih konsisten berdasarkan sistem tipe JSON. Misalnya, dalam v1, nilai
false
dari tipebool
dihilangkan, tetapi dalam v2,false
tidak dihilangkan karena bukan nilai JSON yang kosong. Dalam v2, opsiomitzero
ditambahkan untuk menggantikan perilakuomitempty
v1 yang diterapkan pada0
ataufalse
. - Opsi Terkait: Jika Anda menginginkan perilaku yang sama dengan v1 dalam v2, gunakan opsi
json.OmitEmptyWithLegacyDefinition(true)
.
3. Perubahan Perilaku Opsi Tag string
- Perilaku v1: Diterapkan pada bidang tipe angka, boolean, dan string. Nilai tersebut di-encode ulang di dalam string JSON (misalnya:
int(42)
->"42"
). Tidak diterapkan secara rekursif pada nilai di dalam tipe komposit (slice, map, dll.). - Perilaku v2: Hanya diterapkan pada tipe angka dan diterapkan secara rekursif. Artinya, angka di dalam slice seperti
[]int
juga akan di-encode sebagai string JSON. - Alasan Perubahan: Tujuan utama opsi
string
adalah untuk merepresentasikan angka sebagai string untuk mencegah hilangnya presisi pada bilangan bulat 64-bit. Perilaku v1 terbatas dan kurang konsisten. v2 berfokus pada tujuan inti ini dan memperluas perilakunya secara rekursif untuk membuatnya lebih berguna. - Opsi Terkait: Anda dapat meniru perilaku v1 dengan opsi
json.StringifyWithLegacySemantics(true)
.
4. Marshaling Slice dan Map nil
- Perilaku v1: Slice
nil
dan mapnil
di-marshal sebagainull
. - Perilaku v2: Secara default, slice
nil
di-marshal sebagai[]
(array kosong), dan mapnil
di-marshal sebagai{}
(objek kosong). - Alasan Perubahan:
nil
adalah detail implementasi bahasa Go, dan mengeksposnya ke format JSON yang tidak tergantung bahasa tidaklah diinginkan.[]
atau{}
yang mewakili koleksi kosong adalah ekspresi yang lebih umum. - Opsi Terkait: Dalam v2, Anda dapat me-marshal sebagai
null
seperti v1 melalui opsijson.FormatNilSliceAsNull(true)
ataujson.FormatNilMapAsNull(true)
.
5. Unmarshaling Array
- Perilaku v1: Ketika unmarshaling ke array Go (
[N]T
), tidak akan menghasilkan kesalahan meskipun panjang array JSON berbeda dengan panjang array Go. Jika panjangnya lebih pendek, sisa ruang akan diisi dengan nilai nol, dan jika lebih panjang, kelebihannya akan dibuang. - Perilaku v2: Panjang array JSON harus sama persis dengan panjang array Go. Jika tidak, akan terjadi kesalahan.
- Alasan Perubahan: Dalam Go, array berukuran tetap seringkali memiliki arti penting. Perilaku v1 dapat menyebabkan hilangnya data secara diam-diam. v2 meningkatkan akurasi dengan aturan yang lebih ketat.
- Opsi Terkait: Anda dapat meniru perilaku v1 dengan opsi
json.UnmarshalArrayFromAnyLength(true)
.
6. Pemrosesan time.Duration
- Perilaku v1:
time.Duration
diperlakukan secara internal sebagaiint64
dan di-encode sebagai angka JSON dalam unit nanodetik. - Perilaku v2: Menggunakan metode
time.Duration.String()
untuk di-encode sebagai string JSON dalam format seperti"1h2m3s"
. - Alasan Perubahan: Nanodetik angka kurang terbaca, dan representasi string standar dari
time.Duration
lebih berguna. - Opsi Terkait: Anda dapat menggunakan perilaku v1 melalui opsi tag
json:",format:nano"
atau opsijson.FormatTimeWithLegacySemantics(true)
.
7. Pemrosesan UTF-8 yang Tidak Valid
- Perilaku v1: Saat marshaling/unmarshaling, jika ada byte UTF-8 yang tidak valid dalam string, secara diam-diam diganti dengan karakter pengganti Unicode (
\uFFFD
). - Perilaku v2: Secara default, jika menemukan UTF-8 yang tidak valid, mengembalikan kesalahan.
- Alasan Perubahan: Untuk mencegah kerusakan data secara diam-diam dan untuk mematuhi standar JSON yang lebih ketat (RFC 7493).
- Opsi Terkait: Anda dapat meniru perilaku v1 dengan opsi
jsontext.AllowInvalidUTF8(true)
.
8. Pemrosesan Nama Anggota Objek Duplikat
- Perilaku v1: Mengizinkan nama anggota yang sama muncul berulang kali di dalam objek JSON. Nilai yang muncul terakhir akan menimpa nilai sebelumnya.
- Perilaku v2: Secara default, jika ada nama anggota duplikat, mengembalikan kesalahan.
- Alasan Perubahan: Standar RFC 8259 tidak mendefinisikan perilaku nama duplikat, sehingga dapat berperilaku berbeda di setiap implementasi. Ini dapat menjadi sumber kerentanan keamanan. v2 secara eksplisit menolak ini untuk meningkatkan akurasi dan keamanan.
- Opsi Terkait: Anda dapat meniru perilaku v1 dengan opsi
jsontext.AllowDuplicateNames(true)
.
Perbedaan Implementasi dan Arsitektur
- v1: Sangat bergantung pada
decodeState
didecode.go
dan state machine yang ditulis secara manual discanner.go
. Ini adalah struktur monolitik di mana logika parsing dan analisis semantik sangat erat terikat. - v2: Arsitekturnya lebih modular.
encoding/json/jsontext
: Menyediakan tokenizer JSON (Decoder
) dan encoder (Encoder
) berkinerja tinggi tingkat rendah. Paket ini hanya berfokus pada aspek sintaksis JSON.encoding/json/v2
: Berdasarkanjsontext
, menangani konversi semantik antara tipe Go dan nilai JSON.- Pemecahan ini memisahkan analisis sintaksis dan semantik, meningkatkan kejelasan kode dan kinerja.
API dan Fitur Baru v2
v2 menyediakan kemampuan kontrol yang sangat fleksibel melalui sistem json.Options
.
json.Options
: Sekumpulan opsi yang mengubah perilaku marshaling/unmarshaling.json.JoinOptions(...)
: Menggabungkan beberapa opsi menjadi satu.WithMarshalers
/WithUnmarshalers
: Fitur canggih yang memungkinkan injeksi logika serialisasi/deserialisasi untuk tipe tertentu tanpa harus mengimplementasikan antarmukaMarshaler
/Unmarshaler
. Ini sangat berguna saat memproses tipe dari paket eksternal.- Opsi Baru: Berbagai kontrol perilaku yang tidak mungkin dilakukan di v1, seperti
RejectUnknownMembers
,Deterministic(false)
,FormatNilSliceAsNull
, kini tersedia.
Kesimpulan
encoding/json
v2 adalah implementasi modern yang sangat meningkatkan akurasi, kinerja, dan fleksibilitas berdasarkan pengalaman v1. Meskipun perilaku defaultnya lebih ketat, sistem Options
yang canggih mendukung semua perilaku v1 secara sempurna, memungkinkan pengenalan keuntungan v2 secara bertahap sambil mempertahankan kompatibilitas dengan kode yang ada.
- Untuk proyek baru, disarankan untuk menggunakan v2 sebagai standar.
- Proyek yang sudah ada dapat terus menggunakan
jsonv1
, atau mengadopsi strategi migrasi kejsonv2
dengan memperkenalkan perilaku v2 yang lebih ketat secara bertahap melaluiDefaultOptionsV1()
.