Data yang dipertukarkan antarprogram hampir selalu berbentuk berkas dengan format yang disepakati, dan dua format yang paling umum adalah JSON serta CSV. Ruby menyiapkan keduanya di dalam pustaka standar melalui pustaka json dan csv, sehingga penguraian maupun penyusunan data tidak memerlukan pemasangan tambahan. Topik ini membahas JSON.generate dan JSON.parse beserta opsi symbolize_names, penulisan dan pembacaan CSV dengan CSV.open dan CSV.read, pembacaan tabel dengan headers: true, serta pertimbangan memilih di antara kedua format tersebut.
Menghasilkan dan menguraikan JSON#
Pustaka json menyediakan dua metode utama yang saling berkebalikan. JSON.generate mengubah hash atau array Ruby menjadi teks JSON, sedangkan JSON.parse menguraikan teks JSON kembali menjadi hash atau array. Struktur yang bersarang, misalnya array di dalam hash, dipertahankan apa adanya pada kedua arah. Kunci pada hash hasil penguraian berupa string secara bawaan, sehingga aksesnya menggunakan tanda kutip seperti data["lokasi"]. Arah sebaliknya berlaku sama, karena kunci simbol pada hash Ruby ditulis sebagai string pada teks JSON-nya. Untuk berkas yang dibaca manusia, JSON.pretty_generate menghasilkan teks berindentasi dengan isi yang sama.
# menyimpan rekap suhu ke JSON lalu membacanya kembali
require "json"
rekap = {
lokasi: "Ruang Server",
pengukuran: [24.5, 26.0, 25.5]
}
File.write("rekap.json", JSON.generate(rekap))
puts File.read("rekap.json")
data = JSON.parse(File.read("rekap.json"))
puts "Lokasi: #{data["lokasi"]}"
puts "Pengukuran pertama: #{data["pengukuran"][0]}"{"lokasi":"Ruang Server","pengukuran":[24.5,26.0,25.5]}
Lokasi: Ruang Server
Pengukuran pertama: 24.5Berkas di atas dijalankan dengan perintah ruby rekap-suhu-json.rb. Baris pertama keluaran menunjukkan teks JSON yang tersimpan di rekap.json, yaitu satu baris ringkas tanpa spasi tambahan. Setelah diuraikan, hash hasilnya kembali memiliki kunci lokasi dan pengukuran, dan array di dalamnya tetap berupa array dengan bilangan aslinya. Hasil penguraian dapat digunakan seperti hash Ruby biasa, termasuk mengambil satu anggota array di dalamnya.
Kunci JSON dengan symbolize_names#
Opsi symbolize_names: true pada JSON.parse mengubah kunci hasil penguraian menjadi simbol (symbol), yaitu pengenal yang ditulis dengan tanda titik dua. Gaya simbol membuat akses kunci menjadi lebih ringkas dan lazim digunakan pada kode Ruby modern, sedangkan gaya string lebih dekat dengan bentuk asli teks JSON-nya. Keduanya menghasilkan data yang sama, yang berbeda hanyalah jenis kuncinya. Pemilihan gaya biasanya mengikuti gaya hash pada kode di sekitarnya, bukan isi berkas JSON-nya.
require "json"
teks = '{"lokasi":"Ruang A","suhu":24.5}'
data = JSON.parse(teks, symbolize_names: true)
p data[:lokasi]
p data[:suhu]"Ruang A"
24.5Penguraian dengan opsi tersebut menghasilkan hash berkunci simbol, sehingga aksesnya menggunakan data[:lokasi] alih-alih data["lokasi"]. Nilai suhu tetap berupa bilangan karena JSON menyimpan jenis datanya. Bila gaya simbol dipilih, akses dengan tanda kutip pada hash yang sama akan menghasilkan nil tanpa pesan error, sehingga gaya kunci perlu dijaga agar seragam.
Menulis dan membaca CSV#
Pustaka csv menangani data tabular, yaitu data berbentuk tabel dengan baris dan kolom yang seragam. Penulisan berkas dilakukan dengan CSV.open ber-mode “w” yang menyerahkan objek penulis kepada blok, lalu setiap csv << menambahkan satu baris dari sebuah array nilai. Pembacaan sebaliknya menggunakan CSV.read, yang mengembalikan array dari array berisi seluruh baris berkas. Alternatif lain untuk penulisan adalah CSV.generate atau CSV.generate_line, yang menghasilkan teks CSV untuk kemudian ditulis dengan File.write. Nilai yang mengandung koma otomatis diberi tanda kutip oleh penulisnya, sehingga isi sel tidak mengganggu pembagian kolom.
# menulis lalu membaca daftar piket dalam format CSV
require "csv"
CSV.open("piket.csv", "w") do |csv|
csv << ["Senin", "Ali"]
csv << ["Selasa", "Budi"]
csv << ["Rabu", "Citra"]
end
puts File.read("piket.csv")
CSV.read("piket.csv").each do |baris|
puts "#{baris[0]}: #{baris[1]}"
endSenin,Ali
Selasa,Budi
Rabu,Citra
Senin: Ali
Selasa: Budi
Rabu: CitraBerkas di atas dijalankan dengan perintah ruby piket-csv.rb. Tiga baris pertama keluaran adalah isi mentah piket.csv, yaitu nilai-nilai yang dipisahkan koma pada setiap baris. Setelah dibaca dengan CSV.read, setiap baris menjadi array dua anggota, sehingga hari dan nama petugas diambil melalui indeks 0 dan 1. Pembacaan indeks seperti ini tepat untuk tabel tanpa baris judul.
CSV dengan headers: true#
Banyak berkas CSV memiliki baris pertama berisi nama kolom, dan opsi headers: true memanfaatkan baris itu secara penuh. Dengan opsi ini, CSV.read mengembalikan tabel yang setiap barisnya berupa objek CSV::Row, dan nilai diakses melalui nama kolomnya alih-alih nomor indeks. Bentuk akses seperti ini membuat kode tidak terganggu ketika urutan kolom berubah. Objek tabelnya menampung nama kolom pada atribut headers, dan setiap baris dapat dikonversi menjadi hash melalui to_h.
# membaca tabel nilai ujian dengan headers: true
require "csv"
File.write("nilai.csv", "nama,nilai\nAli,82\nBudi,91\nCitra,77\n")
CSV.read("nilai.csv", headers: true).each do |baris|
puts "#{baris["nama"]} memperoleh #{baris["nilai"]}"
endAli memperoleh 82
Budi memperoleh 91
Citra memperoleh 77Berkas di atas dijalankan dengan perintah ruby nilai-csv.rb. Baris pertama berkas berisi nama kolom nama dan nilai, sehingga setiap baris berikutnya dapat diambil dengan baris["nama"] dan baris["nilai"]. Nilai yang diambil tetap berupa teks, dan konversi menjadi bilangan dilakukan dengan to_i atau to_f apabila nilainya akan dihitung.
Memilih JSON atau CSV#
Kedua format melayani kebutuhan yang berbeda, dan pilihannya mengikuti bentuk data yang akan disimpan. JSON unggul pada data bersarang karena hash dan array bertingkatnya dipertahankan, sedangkan CSV unggul pada tabel datar dengan kolom yang seragam. Teks JSON pada tabel yang panjang memboroskan ruang karena nama kuncinya ditulis ulang pada setiap baris, sedangkan CSV hanya menyimpan nilainya sekali per sel. Tabel berikut merangkum perbedaan yang paling menentukan.
| Aspek | JSON | CSV |
|---|---|---|
| Struktur | bersarang, mendukung hash dan array bertingkat | tabular datar berupa baris dan kolom |
| Tipe nilai | angka, teks, boolean, dan null dipertahankan | seluruh nilai berupa teks |
| Bentuk dokumen | satu dokumen dengan isi campuran | tabel dengan kolom yang seragam |
| Penggunaan umum | konfigurasi dan pertukaran data antarprogram | rekap nilai, daftar piket, dan ekspor tabel |
Sifat nilai CSV yang seluruhnya berupa teks dapat diverifikasi langsung, termasuk nilai yang semula ditulis sebagai bilangan.
require "csv"
File.write("satu-baris.csv", CSV.generate_line(["Ali", 82]))
baris = CSV.read("satu-baris.csv").first
p baris[0].class
p baris[1].classString
StringBilangan 82 dikirim sebagai bagian baris CSV, tetapi hasil pembacaannya berupa String sebagaimana anggota lainnya. Perilaku ini berbeda dengan JSON yang mempertahankan jenisnya, sehingga data CSV selalu memerlukan konversi eksplisit sebelum dihitung. Karena itu, tabel rekap sederhana cocok disimpan sebagai CSV, sedangkan data bertingkat seperti konfigurasi lebih sesuai dalam JSON.
Kesalahan umum#
Kesalahan pertama adalah menggunakan JSON atau CSV tanpa memuat pustakanya terlebih dahulu. Nama konstanta tersebut belum ada sebelum require dijalankan, dan programnya berhenti karena NameError.
begin
JSON.generate({ suhu: 24.5 })
rescue NameError => e
puts "#{e.class}: #{e.message}"
endNameError: uninitialized constant JSONPerbaikannya sederhana, yaitu menambahkan require "json" pada bagian atas berkas sebelum konstanta JSON digunakan. Syarat yang sama berlaku untuk require "csv".
Kesalahan kedua adalah menguraikan teks JSON yang tidak lengkap tanpa penanganan. JSON.parse membangkitkan JSON::ParserError untuk teks yang tidak membentuk JSON sah, misalnya kurung penutup yang hilang.
require "json"
begin
JSON.parse('{"suhu": 24.5')
rescue JSON::ParserError => e
puts e.class
endJSON::ParserErrorTeks contoh kehilangan kurung kurawal penutupnya, sehingga penguraian gagal dan cabang rescue menangkapnya. Data JSON dari luar program memang perlu diperlakukan dengan penangkapan semacam ini karena bentuknya tidak dapat dijamin.
Kesalahan ketiga adalah mengakses baris CSV dengan nama kolom tanpa menggunakan headers: true. Hasil pembacaan tanpa opsi itu berupa array biasa, sehingga baris["nama"] mengembalikan nil dan program melanjutkan dengan data kosong. Gunakan headers: true ketika berkasnya memiliki baris judul, atau akses melalui indeks ketika tidak ada.
Ringkasan#
require "json"danrequire "csv"memuat pustaka standar untuk kedua format tanpa pemasangan gem tambahan.JSON.generatemenghasilkan teks JSON dari hash dan array, sedangkanJSON.parsemenguraikannya kembali dengan kunci string atau simbol melaluisymbolize_names: true.CSV.openber-mode “w” menulis baris dengan operator<<, sedangkanCSV.readmengembalikan array dari array baris berkas.headers: truemenjadikan setiap baris diakses lewat nama kolom, dan seluruh nilai CSV tetap berupa teks yang perlu dikonversi sebelum dihitung.- JSON cocok untuk data bersarang dan pertukaran antarprogram, sedangkan CSV cocok untuk tabel datar dengan kolom seragam.
Referensi#
- JSON — Ruby — Dokumentasi pustaka JSON standar beserta JSON.generate dan JSON.parse.
- CSV — ruby/csv — Repositori resmi pustaka csv beserta dokumentasi penggunaannya pada README.