JSON ke Go
Menghasilkan struct Go dengan tag json dari JSON: angka diberi tipe menurut cara penulisannya, pointer di tempat nilai bisa hilang — semua di peramban Anda.
type Root struct {
Orders []Order `json:"orders"`
HasMore bool `json:"has_more"`
}
type Order struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
CreatedAt string `json:"created_at"`
Price float64 `json:"price"`
Coupon *string `json:"coupon"`
GiftNote *string `json:"gift_note,omitzero"`
Shipping Shipping `json:"shipping"`
Tags []any `json:"tags"`
}
type Shipping struct {
City string `json:"city"`
Postcode json.RawMessage `json:"postcode"`
}
Di setiap posisi di bawah, setiap angka dibaca sebagai bilangan bulat, jadi tipenya int64. Nilai yang ditulis dengan titik desimal atau eksponen, bahkan 10.0, tidak akan dapat didekode ke dalamnya.
Posisi: 2
Order.IDOrder.UserID
Di setiap posisi di bawah, nilainya lebih dari satu jenis — misalnya angka dan string — jadi tipenya json.RawMessage, yang menyimpan setiap nilai persis seperti tertulis di JSON Anda agar program Anda mendekodenya setelah tahu jenisnya. Jika salah satu jenisnya berupa objek, struct-nya tetap ada di keluaran, sehingga nilai itu dapat didekode ke dalamnya.
Posisi: 1
Shipping.Postcode
Di setiap posisi di bawah, tidak ada yang dapat dijadikan dasar untuk menyimpulkan tipe: di sana hanya terlihat null, array-nya selalu kosong, objeknya tidak punya kunci, atau nilainya bersarang terlalu dalam untuk diikuti alat ini. Jadi tipenya any, yang menerima nilai apa pun, atau map[string]any jika objeknya tidak punya kunci, yang menerima objek apa pun atau null dan tidak menerima yang lain.
Posisi: 1
Order.Tags[]
Struct yang mendekode JSON asalnya
Tempel sampel JSON dan halaman ini menuliskan deklarasi Go untuknya: struct bernama untuk setiap objek dalam sampel, dan pada setiap bidang, tag json yang menyebut kunci yang dibaca bidang itu. Satu janji menentukan semua yang ada di bawah. Jika dimasukkan ke dalam program yang dikompilasi dengan Go 1.27, deklarasi itu mendekode JSON yang Anda tempel melalui "encoding/json" tanpa galat, dan setiap kunci dibaca ke dalam bidang, kecuali kunci yang tidak dapat dibawa tag struct mana pun: kunci seperti itu dibiarkan di luar struct. Di tempat Go tidak membiarkan janji itu berlaku, atau memaksa halaman ini memilih sesuatu yang tidak diputuskan data Anda, pemberitahuan di bawah keluaran menyebutkan di mana, dengan satu pengecualian langka yang dijelaskan di bagian tentang tag.
Bentuknya sendiri disusun sebelum ada Go yang ditulis, dan halaman JSON ke TypeScript mendapatkan bentuk itu dari pembacaan yang sama atas JSON Anda. Menggabungkan elemen array, memperhatikan kunci yang hanya dimiliki sebagian elemen, memisahkan null dari kunci yang tidak pernah dikirim, dan menamai setiap objek yang ditemukan di dalam objek lain: panduan halaman itu membahas semuanya, jadi tidak satu pun diulang di bawah. Yang berikut adalah bagian Go — tipe untuk setiap angka, pointer, tipe untuk kunci yang nilainya berbeda jenis, nama bidang, tag, dan pertanyaan yang dimunculkan pemberitahuan.
Tiga tipe angka, dipilih dari cara setiap angka ditulis
Peramban yang membaca JSON menjadikan 10 dan 10.0 angka yang sama. Go tidak: bidang "int64" menolak angka yang ditulis dengan titik desimal atau eksponen — 10.0, 1e3, bahkan -0.0 — sementara ia menerima 11 dan -0. Jadi halaman ini tidak memilih tipe angka dari nilainya saja. Ia menanyakan kepada pengurai peramban itu sendiri bagaimana setiap angka ditulis, lalu memutuskan dari situ:
- Di tempat setiap angka pada suatu posisi ditulis sebagai bilangan bulat, tanpa titik desimal dan tanpa eksponen, bidangnya "int64", dan pemberitahuan tentang bilangan bulat menyebutnya.
- Angka lainnya "float64", jadi harga yang ditulis 10.0 adalah "float64" bahkan ketika setiap harga dalam sampel bulat. Modul "json" Python menulis float bernilai bulat persis seperti itu, sehingga API yang disajikan oleh Python adalah tempat yang biasa untuk menjumpainya.
- "json.Number" disediakan untuk angka yang tidak dapat disimpan dengan persis oleh tipe mana pun dari keduanya: bilangan bulat yang melewati salah satu ujung rentang "int64", angka yang terlalu besar untuk dapat disimpan "float64" sama sekali, seperti 1e999, dan bilangan bulat yang terlalu panjang untuk "float64" di samping pecahan, seperti pada [9007199254740993, 1.5]. Ia menyimpan masing-masing dengan persis, dan program Anda mengonversinya di tempat program itu memakai nilainya.
Bilangan bulat panjang di antara bilangan bulat lain tidak memerlukan kehati-hatian seperti itu: [9007199254740993, 1] adalah "[]int64". Pecahan dengan lebih banyak digit daripada yang disimpan "float64" tetap menjadi "float64" dan disebut dalam pemberitahuannya sendiri, karena setelah didekode lalu dikodekan lagi, 0.30000000000000001 kembali sebagai 0.3.
Membaca cara angka ditulis memerlukan peramban yang melaporkannya. Peramban yang tidak melaporkannya membaca setiap angka dari nilainya saja, jadi di sana 10.0 tampak bulat dan bidangnya menjadi "int64", bidang yang tidak akan mendekode JSON itu; halaman ini menghitung angka yang tidak dapat dibedakannya dari bilangan bulat dan menunjukkan di mana yang pertama. Peramban seperti itu juga tidak dapat melihat digit yang dibuang "float64", jadi di sana bilangan bulat panjang di samping pecahan diberi tipe "float64", dan pecahan yang dibulatkan tidak disebut.
Pointer di tempat nilai bisa tidak ada
Kunci yang tidak ada di sebagian objek, atau bernilai null, menjadi pointer: "*string", "*int64", atau pointer ke struct yang ditulis untuk suatu objek. Elemen dari array yang memuat null juga menjadi pointer, jadi [1, null] adalah "[]*int64". Kunci yang tidak ada di sebagian objek juga ditandai "omitzero" di dalam tag. Dengan keduanya terpasang, bidang dapat menunjukkan apakah ada nilai yang datang, dan saat struct hasil dekode dikodekan lagi, Go tidak menulis kunci yang di sampel hanya pernah tidak ada, dan menulis sebagai null kunci yang hanya pernah bernilai null.
Slice, map, "any", dan "json.RawMessage" tidak diberi pointer, karena masing-masing sudah nil ketika tidak ada yang didekode ke dalamnya. Slice tetap mendapat "omitzero" di tempat kuncinya bisa tidak ada, dan array yang ada tetapi kosong ditulis kembali sebagai [].
Satu pembedaan tidak bertahan. Kunci yang tidak ada di sebagian objek dan bernilai null di objek lain menjadi satu nil di Go, jadi program Anda tidak dapat membedakan keduanya, dan saat struct dikodekan lagi, kunci itu tidak ditulis bahkan di tempat sampel berisi null; halaman ini menyebut setiap bidang yang mengalaminya. Kunci yang nilainya berjenis campuran mempertahankan perbedaan itu, karena "json.RawMessage" menyimpan null sebagai bita null.
json.RawMessage untuk jenis campuran, dan any di tempat tidak ada yang terlihat
Ketika nilai suatu kunci berbeda jenis — angka di satu tempat, string atau objek di tempat lain — bidangnya "json.RawMessage": bita nilai itu sendiri, dibiarkan untuk didekode program Anda setelah program itu melihat jenis apa yang datang. Null di antaranya tidak mengubah apa pun. Di tempat salah satu jenisnya berupa objek, struct yang diekstrak untuk objek itu tetap dicetak di samping struct lainnya. "any" juga akan menerima setiap jenis, dan ia tidak dipakai untuk campuran, karena angka yang didekode ke dalam "any" menjadi "float64" dan kehilangan apa pun yang tidak dapat ditampung "float64".
Ada bagian sampel yang tidak memberi nilai apa pun untuk dipelajari: kunci yang hanya pernah berisi null, array yang kosong setiap kali, objek tanpa kunci, dan nilai yang terkubur lebih dalam daripada yang dibaca inferensi. Go mendapat "any" untuk kunci null dan nilai yang terkubur itu, "[]any" untuk elemen dari array yang kosong, dan "map[string]any" untuk objek itu. "any" menerima setiap jenis nilai. Map menerima objek atau null, dan string, angka, array, atau boolean di tempatnya membuat pendekodean gagal.
Di luar kedalaman itu, ada dua hal yang tidak dikatakan pemberitahuan. Kunci di sana yang tidak ada di sebagian objek dan bernilai null di objek lain hanya disebut sebagai posisi yang tidak memberi dasar untuk menyimpulkan tipe, dan saat struct dikodekan lagi, Go tidak menulis kunci itu di tempat ia bernilai null. Dan bilangan bulat yang lebih panjang daripada yang dapat ditampung "float64" kembali dalam keadaan dibulatkan, 9007199254740993 kembali sebagai 9007199254740992, karena posisi itu adalah "any".
Nama bidang bergaya Go, nama struct yang sama dengan halaman TypeScript
Nama bidang adalah ejaan Go untuk kuncinya. Kunci dipecah menjadi kata di setiap karakter yang bukan huruf atau digit, dan di mana pun huruf kecil diikuti huruf kapital; kata-kata itu lalu digabung, masing-masing diawali huruf kapital, jadi "user_name", "last-name", dan "firstName" menjadi UserName, LastName, dan FirstName. Kata yang ada di daftar akronim bawaan staticcheck ditulis dengan huruf kapital — "id" menjadi ID, "api_key" menjadi APIKey, "video_url" menjadi VideoURL — tetapi hanya kata utuh yang dihitung, jadi "idle" menjadi Idle dan bentuk jamak "ids" menjadi Ids. Kunci yang seluruhnya ditulis dengan huruf kapital dibaca sebagai kata-kata: "USER_ID" menjadi UserID.
Huruf kunci itu sendiri dipertahankan, jadi "имя" menjadi Имя. Di tempat nama tidak akan diawali huruf kapital — kunci dalam aksara tanpa huruf kapital, seperti "名前", kunci yang diawali digit, seperti "1st", atau huruf pertama yang tidak punya bentuk kapital satu huruf, seperti "ß" — nama itu mendapat X di depan, seperti X名前, X1st, dan Xß, dan bidangnya tetap membaca kuncinya. Tanda gabungan dibuang dari nama dan dipertahankan di tag. Kunci yang sama sekali tidak memiliki huruf atau digit, seperti "@", dinamai Field, dan nama yang sudah dipakai di struct yang sama mendapat angka: "user_id", "userId", dan "USER_ID" bersama-sama menjadi UserID, UserID2, dan UserID3.
Gaya Go menjangkau nama bidang dan berhenti di situ: setiap struct mempertahankan nama yang dipilih inferensi untuk objeknya, jadi deklarasi di sini dan antarmuka di halaman TypeScript dinamai sama, dan ejaan bidang bisa berbeda dari ejaan struct yang dimuat bidang itu. Cara nama itu dipilih dijelaskan dalam panduan TypeScript.
Tag untuk Go 1.27, dan kunci yang tidak dapat dibawa tag mana pun
Tag setiap bidang menyebut kuncinya dengan persis, seperti pada json:"user_id", dengan ,omitzero setelah kunci di tempat kunci itu bisa tidak ada. Karakter kontrol dalam kunci ditulis dengan escape yang dipakai Go sendiri, dan kunci "-" ditulis json:"-,", bentuk yang dibaca Go sebagai kunci itu.
Tag ditulis untuk Go 1.27. Go 1.26 membaca lebih sedikit kunci melalui tag: kunci yang memuat apa pun selain huruf, digit, spasi ASCII, dan sekumpulan tanda baca ASCII — "Price (€)", "temp °C", emoji, karakter kontrol, aksen gabungan — tidak dibaca di sana, dan halaman ini menyebut setiap bidang seperti itu. Separuh itu diperiksa terhadap Go 1.27 yang dikompilasi dengan "GOEXPERIMENT=nojsonv2", build yang membaca tag seperti Go 1.26 tetapi dengan tabel Unicode Go 1.27 yang lebih baru, jadi kunci yang memuat huruf yang tidak ada di tabel Go 1.26 yang lebih lama dibaca dalam pemeriksaan itu dan tidak memunculkan pemberitahuan.
Kunci yang memuat koma, garis miring terbalik, tanda kutip ganda, apostrof, atau backtick, atau kunci kosong, adalah kunci yang tidak dapat dibawa tag struct mana pun, jadi kunci itu sama sekali tidak mendapat bidang. JSON Anda tetap dapat didekode: "json.Unmarshal" melewati kunci itu tanpa keluhan, meskipun "json.Decoder" yang disetel dengan "DisallowUnknownFields" berhenti di kunci itu. Pemberitahuan mencantumkan kunci seperti itu lewat struct tempatnya berada dan kunci itu sendiri dalam tanda kutip sebagaimana Go mengutip string, misalnya Order["note,internal"], dan struct untuk objek di bawah kunci seperti itu tetap dicetak. Satu kasus lolos tanpa pemberitahuan: dua kunci yang hanya berbeda pada surogat tunggal, yaitu escape yang tidak mewakili karakter apa pun, adalah satu kunci bagi Go, dan tidak satu pun bidang keduanya terisi.
Hanya deklarasi, dalam tata letak gofmt
Keluaran berisi deklarasi tipe dan tidak berisi apa pun selain itu: tanpa klausa package dan tanpa import, jadi keluaran itu masuk ke berkas yang sudah Anda punya, di bawah klausa package berkas itu sendiri. Di tempat keluaran memakai "json.RawMessage" atau "json.Number", berkas itu juga mengimpor "encoding/json", dan itulah baris yang tersisa untuk Anda atau penyunting Anda. Tata letaknya adalah tata letak gofmt sendiri — tab sebelum setiap bidang, dan nama, tipe, serta tag setiap struct dalam kolom yang disejajarkan — jadi gofmt membiarkannya persis seperti adanya. Tipe akar dicetak lebih dulu, setiap objek menjadi tipe bernama, bukan struct yang ditulis di dalam bidang yang memuatnya, dan JSON yang di tingkat teratas berupa array atau nilai tunggal juga menjadi tipe bernama, seperti "type Root []RootItem", sehingga selalu ada tipe yang dapat menjadi tujuan pendekodeannya.
Apa yang diminta pemberitahuan untuk Anda periksa
Setiap jenis keputusan yang dipaksakan Go mendapat satu entri di bawah deklarasi, dan entri itu mengumpulkan setiap tempat yang dikenainya, jadi sampel dengan banyak bidang bilangan bulat mendapat satu entri, bukan satu entri per bidang. Tempat dieja sebagaimana deklarasi mengejanya — Order.UserID adalah bidang, Order.Tags[] adalah elemen array, dan akar adalah nama polosnya — kecuali pada dua entri yang sebagai gantinya menunjuk ke dalam JSON Anda lewat baris dan kolom: kunci yang ditulis dua kali, dan angka yang tidak dapat dibedakan peramban ini dari bilangan bulat. Tidak satu pun pemberitahuan mengubah deklarasi. Bacalah masing-masing sebagai pertanyaan tentang data Anda:
- Bilangan bulat bertipe "int64". Setiap angka di sana ditulis sebagai bilangan bulat. Jika nilai yang datang kemudian bisa membawa titik desimal atau eksponen, seperti yang bisa terjadi pada harga atau pengukuran, nilai itu tidak akan dapat didekode, jadi jadikan bidang itu "float64"; ID atau hitungan bisa dibiarkan seperti adanya.
- "json.Number". Tidak ada tipe angka lain yang dapat menyimpan nilai-nilai itu dengan persis. Konversikan masing-masing di tempat program Anda memakainya, atau, jika Anda tahu nilai sebenarnya muat dalam tipe yang lebih sempit, ubahlah sendiri bidang itu.
- Pecahan yang dibulatkan "float64". Angka di sana memiliki lebih banyak digit daripada yang disimpan "float64", jadi program Anda melihat nilai yang mendekatinya, bukan nilai yang tertulis. Di tempat setiap digit penting, jadikan bidang itu "json.Number".
- Jenis campuran yang disimpan sebagai "json.RawMessage". Periksa setiap nilai saat nilai itu datang dan dekode sebagai jenis yang ternyata dimilikinya; di tempat salah satu jenisnya berupa objek, struct objek itu tetap ada di keluaran, sehingga nilai itu dapat didekode ke dalamnya.
- Tidak ada yang dapat dijadikan dasar untuk menyimpulkan tipe. Bidangnya "any", atau map berisi "any", karena sampel tidak memuat nilai apa pun di sana untuk dipelajari. Gantilah dengan tipe yang Anda tahu dibawa bidang itu, atau konversi lagi dari JSON yang di tempat itu berisi nilai sungguhan.
- Tidak ada di sebagian objek, null di objek lain. Go menyimpan satu nil untuk keduanya, jadi saat struct dikodekan lagi, kunci itu tidak ditulis. Hal itu hanya penting jika apa pun yang membaca keluaran Anda memperlakukan null secara berbeda dari kunci yang tidak ada.
- Kunci yang tidak dibaca Go 1.26. Kompilasi dengan Go 1.27, atau ganti nama kunci-kunci itu di tempat kunci itu dihasilkan; di Go 1.26 kunci-kunci itu tidak dibaca.
- Kunci yang dibiarkan di luar struct. Kunci itu tidak punya bidang. Untuk membacanya, dekode objek ke "map[string]json.RawMessage" atau tulis metode "UnmarshalJSON" untuk struct itu, seperti yang disarankan pemberitahuan itu sendiri.
- Kunci yang ditulis dua kali. Tipenya mengikuti salinan terakhir kunci itu, tetapi Go mendekode setiap salinan secara berurutan, jadi salinan sebelumnya yang tidak dapat ditampung tipe itu, seperti string di tempat salinan terakhirnya berupa angka, membuat pendekodean mengembalikan galat, dan kunci yang hanya ada di salinan sebelumnya dari suatu objek tidak dibaca. Perbaiki JSON pada baris dan kolom yang ditunjukkan.
- Peramban ini tidak dapat membedakan 10 dari 10.0. Peramban ini tidak melaporkan cara angka ditulis, jadi bidang yang diberi tipe "int64" berdasarkan nilai yang ditulis dengan titik desimal atau eksponen tidak akan dapat mendekode JSON Anda. Periksa bidang yang nilainya ditulis dengan cara itu, atau konversi JSON itu di peramban yang melaporkannya.
Tidak ada pemberitahuan tentang tanggal, format, atau himpunan nilai yang sudah ditentukan, karena halaman ini tidak pernah menebak salah satunya dari string: "created_at" dalam contoh tetap "string", seperti apa pun rupanya.
Pertanyaan yang sering diajukan
- Mengapa harga saya float64 padahal setiap harga di JSON saya adalah angka bulat?
- Karena setiap harga ditulis dengan titik desimal, seperti 10.0, dan bidang "int64" menolak angka yang ditulis dengan cara itu, bulat atau tidak. Halaman ini membaca cara setiap angka ditulis, bukan hanya nilainya, jadi bidangnya "float64" dan JSON Anda dapat didekode. Jika bidang itu diberi tipe "int64" berdasarkan nilainya saja, pendekodean akan berhenti di harga pertama.
- Kapan angka keluar sebagai json.Number?
- Ketika salah satu nilainya adalah bilangan bulat di luar rentang "int64", angka yang terlalu besar untuk "float64", atau bilangan bulat panjang yang terletak di samping pecahan dan akan dibulatkan "float64". "json.Number" menjaga setiap nilai itu tetap persis, dan pemberitahuan di bawah keluaran mencantumkan setiap bidang yang diberi tipe itu.
- Mengapa sebagian bidang berupa pointer, dan apa yang dilakukan omitzero di tag?
- Bidang menjadi pointer di tempat kuncinya tidak ada di sebagian objek atau bernilai null di sebagian objek, sehingga nil dapat menunjukkan bahwa tidak ada nilai yang datang. "omitzero" dipasang di tempat kunci tidak ada di sebagian objek: saat struct dikodekan lagi, kunci itu lalu tidak ditulis, sama seperti di JSON Anda, alih-alih ditulis sebagai null.
- Mengapa json.RawMessage untuk kunci dengan jenis campuran, dan bukan any?
- "json.RawMessage" menyimpan bita setiap nilai sampai program Anda memutuskan cara membacanya. "any" juga akan menerima nilai-nilai itu, tetapi angka yang didekode ke dalam "any" adalah "float64", dan bilangan bulat panjang kehilangan digit terakhirnya di sana.
- Mengapa sebagian nama bidang diawali X?
- Karena kalau tidak, nama itu tidak akan diawali huruf kapital: kunci itu ditulis dalam aksara tanpa huruf kapital, diawali digit, atau diawali huruf yang tidak punya bentuk kapital satu huruf. X mempertahankan huruf kunci itu sendiri di dalam nama, dan bidangnya tetap membaca kuncinya — X名前 membaca "名前".
- Versi Go mana yang diperlukan struct ini?
- Struct itu ditulis dan diperiksa untuk Go 1.27. Cara Go 1.26 membaca tag juga diperiksa, melalui pengganti: Go 1.26 tidak membaca kunci yang memuat karakter di luar huruf, digit, spasi ASCII, dan sekumpulan tanda baca ASCII, seperti simbol mata uang atau emoji, dan halaman ini menyebut setiap bidang yang kuncinya terkena hal itu — kecuali kunci yang memuat huruf yang lebih baru daripada tabel Unicode Go 1.26: kunci seperti itu tidak dapat dilihat oleh halaman ini maupun oleh pemeriksaan itu.
- Mengapa salah satu kunci saya tidak ada di struct?
- Karena kunci itu memuat koma, garis miring terbalik, tanda kutip ganda, apostrof, atau backtick, atau kosong, dan tidak ada tag struct yang dapat membawa kunci seperti itu. Pemberitahuan mencantumkannya lewat struct tempatnya berada dan kunci itu sendiri, seperti Root["a,b"]; sisa JSON Anda tetap dapat didekode, dan pemberitahuan itu menjelaskan bagaimana kunci itu dapat dibaca dengan cara lain.
- Amankah menempel respons yang memuat token atau kata sandi?
- Ya. Setiap langkah berjalan di dalam halaman ini di komputer Anda, dan respons yang Anda tempel tidak pergi ke tempat lain: tidak ada server yang menerimanya dan tidak ada yang menyimpan salinannya. Deklarasi yang dihasilkan hanya membawa nama bidang, tag, dan tipe Go, jadi token atau kata sandi di dalam JSON tidak meninggalkan jejak apa pun di deklarasi itu selain bidang "string" yang dinamai menurut kuncinya.
Alat terkait
- JSON ke TypeScript
Di tempat nilai suatu kunci lebih dari satu jenis, halaman ini menyimpan setiap nilai persis seperti tertulis di JSON Anda agar program Anda mendekodenya, karena Go tidak punya union. Halaman itu menulis bentuk yang sama sebagai tipe TypeScript dengan nama tipe yang sama, dengan union di tempat itu, dan panduannya menjelaskan cara bentuk itu disusun.
- JSON ke Zod
Go tidak punya tipe yang sekadar angka JSON, jadi di tempat setiap angka ditulis sebagai bilangan bulat, halaman ini memberi bidang itu tipe bilangan bulat, dan nilai yang ditulis dengan titik desimal tidak akan dapat didekode ke dalamnya. Halaman itu menulis bentuk yang sama sebagai skema Zod dengan nama tipe yang sama, dan skema memeriksa data setiap kali data itu tiba serta juga menerima pecahan di bidang seperti itu.
- Penguji JSONPath
Uji kueri JSONPath (RFC 9535) terhadap JSON.
- Pembuat tabel Markdown
Buat dan ratakan tabel Markdown dari CSV, TSV, atau JSON.