Belajar Parsing File XML, CSV, dan TSV di Ionic Bagian 2

Bagus Aji Santoso 3 April 2018

Belajar Parsing File XML, CSV, dan TSV di Ionic Bagian 2

Baca dulu bagian pertama di sini.

Memuat dan Membaca File CSV

Meski CSV bukan jenis format data yang paling enak untuk dipakai bekerja, tapi ada saatnya dimana kita tidak punya pilihan lain selain CSV.

Buka aplikasi spreadsheet (Microsoft Excel atau LibreOffice Calc) lalu buat sebuah tabel dengan struktur sebagai berikut:

idtitlepublishergenre
1ValiantFleetwayAction/Adventure
2VictorIPC MagazinesAction/Adventure/War
3LionIPC MagazinesAction/Adventure/War/Science Fiction/Information
4BattleIPC MagazinesAction/Adventure/War
52000ADIPC MagazinesThriller/Science Fiction/Myth/History
6WarlordIPC MagazinesWar
7EagleIPC MagazinesScience Fiction/Thriller/Adventure
8Transformers UKMarvelScience Fiction/Action

Simpan dengan nama comics.xml (atau, jika menggunakan aplikasi Number di Apple, comics.numbers) lalu ekspor file tersebut dengan nama comics.csv ke folder comics/src/assets/data/.

Dengan file comics.csv tersimpan ditempat yang bisa diakses oleh aplikasi, sekarang kita bisa menuliskan logika untuk mengimpor file dan memparsing datanya.

Modifikasi file comics/src/pages/home/home.ts sehingga memiliki kode berikut di dalam kelas HomePage:

public csvItems : any; constructor(public navCtrl: NavController, public http : HttpClient) { } ionViewWillEnter() { this.loadCSV(); } loadCSV() { this.http.get('/assets/data/comics.csv') .map(res => res.text()) .subscribe((data)=> { var csv = this.parseCSVFile(data); this.csvItems = csv; }); } parseCSVFile(str) { var arr = [], obj = [], row, col, c, quote = false; // true means we're inside a quoted field // iterate over each character, keep track of current row and column (of the returned array) for (row = col = c = 0; c < str.length; c++) { var cc = str[c], nc = str[c+1]; // current character, next character arr[row] = arr[row] || []; arr[row][col] = arr[row][col] || ''; /* If the current character is a quotation mark, and we're inside a quoted field, and the next character is also a quotation mark, add a quotation mark to the current column and skip the next character */ if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; } // If it's just one quotation mark, begin/end quoted field if (cc == '"') { quote = !quote; continue; } // If it's a comma and we're not in a quoted field, move on to the next column if (cc == ',' && !quote) { ++col; continue; } /* If it's a newline and we're not in a quoted field, move on to the next row and move to column 0 of that new row */ if (cc == '\n' && !quote) { ++row; col = 0; continue; } // Otherwise, append the current character to the current column arr[row][col] += cc; } return this.formatParsedObject(arr, true); } formatParsedObject(arr, hasTitles) { let id, title, publisher, genre, obj = []; for(var j = 0; j < arr.length; j++) { var items = arr[j]; if(items.indexOf("") === -1) { if(hasTitles === true && j === 0) { id = items[0]; title = items[1]; publisher = items[2]; genre = items[3]; } else { obj.push({ id : items[0], title : items[1], publisher : items[2], genre : items[3] }); } } } return obj; }

Cukup banyak kode yang kita tulis jadi mari kita bahas satu per satu.

Method loadCSV menggunakan method get dari kelas http milik Angular 2 untuk memuat URL file (dalam hal ini adalah file comics.csv), lalu memanggil method map untuk mengubah data yang dimuat menjadi text sebelum mengirimkannya ke method subscribe.

Di dalam observable kita lalu mengirimkan data CSV tersebut ke method parseCSVFile. Setelah selesai melakukan parsing, method ini akan mengembalikan objek JSON yang kemudian disimpan ke dalam properti csvItems untuk dipakai di template HTML.

loadCSV() { this.http.get('/assets/data/comics.csv') .map(res => res.text()) .subscribe((data)=> { var csv = this.parseCSVFile(data); this.csvItems = csv; }); }

Method parseCSVFile ini sedikit lebih rumit, ia meng-handle data CSV yang dikirimka karakter per karakter untuk menentukan tipenya (memeriksa apakah karakter tersebut tanda tanya, koma, atau baris baru misalnya) dan menentukan aksi apa yang harus diambil untuk setiap karakter tersebut.

Proses ini akan menghasilkan konten berbentuk array multidimensi sesuai dengan kolom/baris yang terdapat di data CSV. Konten yang dihasilkan kemudian diproses oleh method formatPrasedObject:

parseCSVFile(str) { var arr = [], obj = [], row, col, c, quote = false; // true means we're inside a quoted field // iterate over each character, keep track of current row and column (of the returned array) for (row = col = c = 0; c < str.length; c++) { var cc = str[c], nc = str[c+1]; // current character, next character arr[row] = arr[row] || []; arr[row][col] = arr[row][col] || ''; /* If the current character is a quotation mark, and we're inside a quoted field, and the next character is also a quotation mark, add a quotation mark to the current column and skip the next character */ if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; } // If it's just one quotation mark, begin/end quoted field if (cc == '"') { quote = !quote; continue; } // If it's a comma and we're not in a quoted field, move on to the next column if (cc == ',' && !quote) { ++col; continue; } /* If it's a newline and we're not in a quoted field, move on to the next row and move to column 0 of that new row */ if (cc == '\n' && !quote) { ++row; col = 0; continue; } // Otherwise, append the current character to the current column arr[row][col] += cc; } return this.formatParsedObject(arr, true); }

Logika untuk method di atas, diambil dari post Stack Overflow berikut dengan sedikit perubahan. Semua kredit harus diberika kepada Trevor Dixon karena telah berbagai solusi yang terbaik.

Method formatParsedObject akan membaca konten CSV yang sebelumnya sudah dibaca oleh parseCSVFile agar berubah menjadi JSON yang bisa dikirimkan lagi ke halaman HTML.

Yang dilakukan oleh method ini hanyalah melakukan looping pada data yang dierimanya, memeriksa apakah ada array kosong yang baru dibuat, memeriksa apakah item pertama di array adalah field titles dari file CSV (dilakukan oleh method hasTitle) dan setelahnya membuat objek-objek individu yang masing-masing memiliki data publikasi. Objek-objek ini lalu dimasukkan ke sebuah array dan dikirim balik dari method ini setelah semua objek selesai dibuat:

formatParsedObject(arr, hasTitles) { let id, title, publisher, genre, obj = []; for(var j = 0; j < arr.length; j++) { var items = arr[j]; if(items.indexOf("") === -1) { if(hasTitles === true && j === 0) { id = items[0]; title = items[1]; publisher = items[2]; genre = items[3]; } else { obj.push({ id : items[0], title : items[1], publisher : items[2], genre : items[3] }); } } } return obj; }

Sekarang yang perlu kita lakukan adalah menulis kode HTML berikut ke file comics/src/pages/home/home.html:

<ion-item-group> <ion-item-divider color="light">CSV</ion-item-divider> <ion-item *ngFor="let item of csvItems"> {{ item.title }} </ion-item> </ion-item-group>

Tambahkan kode di atas di bawah kode untuk XML.

<ion-header> <ion-navbar> <ion-title> British Comics of the 60's - 80's </ion-title> </ion-navbar> </ion-header> <ion-content padding> <ion-item-group> <ion-item-divider color="light">XML</ion-item-divider> <ion-item *ngFor="let item of xmlItems"> {{ item.title }} </ion-item> </ion-item-group> <ion-item-group> <ion-item-divider color="light">CSV</ion-item-divider> <ion-item *ngFor="let item of csvItems"> {{ item.title }} </ion-item> </ion-item-group> </ion-content>

Setelah melakuakn perintah ionic serve, kita akan mendapat pesan berikut ini di layar:

Image

Dibandingkan saat memparsing XML, bekerja dengan CSV dan mengubahnya menjadi JSON membutuhkan lebih banyak usaha namun, untungnya juga tidak terlalu sulit.