JsonWriterSax - perpustakaan untuk membuat JSON

Beberapa waktu lalu saya menulis aplikasi c ++ / Qt yang mengirim sejumlah besar data dalam format JSON melalui jaringan. QJsonDocument standar digunakan . Selama implementasi, saya mengalami kinerja yang rendah, serta desain kelas yang tidak nyaman, yang tidak memungkinkan deteksi kesalahan yang normal selama operasi. Hasilnya adalah perpustakaan JsonWriterSax , yang memungkinkan Anda untuk menulis dokumen JSON dengan gaya SAX dengan kecepatan tinggi, yang saya terbitkan di github.com di bawah lisensi MIT. Siapa yang peduli - saya minta kucing.


Sedikit teori


JSON (JavaScript Object Notation) adalah format data teks terstruktur yang dikembangkan oleh Douglas Crockford dan subset dari bahasa script ECMAS (JavaScript, JScript, dll. Dibuat berdasarkan basisnya). JSON menggantikan XML, memperluas kemampuan bersarang dan menambahkan tipe data. Saat ini aktif digunakan di Internet.


Tapi ada kekurangan di JSON. Menurut pendapat saya, di antara tipe standar, jelas tidak ada tipe DateTime yang cukup - Anda harus mentransfer nilai dalam bentuk angka atau string, dan ketika parsing perlu untuk membuat keputusan tergantung pada konteksnya. Tetapi perlu dicatat bahwa dalam ECMAScript, tipe Date dibuat sejak lama, tidak dipikirkan, dan di dunia js, perpustakaan pihak ketiga digunakan untuk bekerja dengan tanggal.


Ada 2 pendekatan utama untuk mem-parsing dan membuat dokumen terstruktur - SAX dan DOM. Mereka muncul untuk XML, tetapi dapat digunakan sebagai pola untuk membuat penangan format lain.


SAX (API Sederhana untuk XML)


Ini digunakan untuk pemrosesan data berurutan dan memungkinkan Anda untuk memproses dokumen besar dalam aliran. Saat membaca, itu kembali ke informasi aplikasi tentang elemen atau kesalahan yang ditemukan, tetapi pelestarian informasi dan kontrol bersarang terletak pada aplikasi itu sendiri. Saat merekam, langkah biasanya ditunjukkan dengan gaya: memulai elemen, memulai sub-elemen, menulis angka, menulis garis, menutup sub elemen, menutup elemen. Kerugiannya termasuk fakta bahwa programmer diharuskan untuk menulis kode lebih teliti, untuk lebih memahami struktur dokumen dan kurangnya atau keterbatasan ekstrim dalam mengedit dokumen yang ada.


DOM (Model Objek Dokumen)


Dengan metode ini, pohon dokumen dibangun dalam memori yang dapat diserialisasi, deserialized, dan dimodifikasi. Kerugian utama adalah konsumsi memori yang tinggi dan peningkatan waktu pemrosesan. Di bawah kap, handler SAX biasanya digunakan.


Masalah Dokumen QJson


QJsonDocument standar menggunakan pendekatan DOM. Saat membuat dokumen, kecepatannya rendah - Anda dapat melihat tolok ukur di akhir artikel. Tapi masalah terbesar bagi saya adalah desain pengembalian kesalahan yang salah.


auto max = std::numeric_limits<int>::max(); QJsonArray ja; for(auto i = 0; i < max; ++i) { ja.append(i); if(ja.size() - 1 != i) { break; } } 

Dalam contoh ini, jika tidak ada cukup memori, pesan akan ditulis ke aliran kesalahan.


QJson: Document too large to store in data structure
dan data tidak akan ditambahkan lagi. Dalam hal array, Anda dapat memeriksa kondisinya


 ja.size() - 1 != i 

Tetapi apa yang harus dilakukan ketika bekerja dengan suatu objek? Terus-menerus memeriksa apakah kunci baru telah ditambahkan? Parsing log in untuk mencari kesalahan?


Perpustakaan


Pustaka JsonWriterSax memungkinkan Anda untuk menulis dokumen JSON dalam QTextStream dengan gaya SAX dan tersedia di github di bawah lisensi MIT. Kontrol memori ada pada aplikasi. Perpustakaan mengontrol integritas JSON - jika elemen ditambahkan secara tidak benar, fungsi tulis akan mengembalikan kesalahan. Untuk kontrol, tata bahasa KS digunakan. Tes telah ditulis, tetapi mungkin beberapa kasus dibiarkan tanpa pengawasan. Jika seseorang memperbaiki operasi cek yang salah dan melaporkan untuk memperbaiki kesalahan - saya akan sangat berterima kasih.


Saya pikir deskripsi terbaik perpustakaan untuk programmer adalah kode example =)


Contohnya


Array penciptaan


 QByteArray ba; QTextStream stream(&ba); stream.setCodec("utf-8"); JsonWriterSax writer(stream); writer.writeStartArray(); for(auto i = 0; i < 10; ++i) { writer.write(i); } writer.writeEndArray(); if(writer.end()) { stream.flush(); } else { qWarning() << "Error json"; } 

Hasilnya, kita dapatkan


 [0,1,2,3,4,5,6,7,8,9] 

Penciptaan Obyek


 QByteArray ba; QTextStream stream(&ba); stream.setCodec("utf-8"); JsonWriterSax writer(stream); writer.writeStartObject(); for(auto i = 0; i < 5; ++i) { writer.write(QString::number(i), i); } for(auto i = 5; i < 10; ++i) { writer.write(QString::number(i), QString::number(i)); } writer.writeKey("arr"); writer.writeStartArray(); writer.writeEndArray(); writer.writeKey("o"); writer.writeStartObject(); writer.writeEndObject(); writer.writeKey("n"); writer.writeNull(); writer.write(QString::number(11), QVariant(11)); writer.write("dt", QVariant(QDateTime::fromMSecsSinceEpoch(10))); writer.writeEndObject(); if(writer.end()) { stream.flush(); } else { qWarning() << "Error json"; } 

Hasilnya, kita dapatkan


 {"0":0,"1":1,"2":2,"3":3,"4":4,"5":"5","6":"6","7":"7","8":"8","9":"9","arr":[],"o":{},"n":null,"11":11,"dt":"1970-01-01T03:00:00.010"} 

Membuat dokumen dengan tipe bersarang dan berbeda


 QByteArray ba; QTextStream stream(&ba); stream.setCodec("utf-8"); JsonWriterSax writer(stream); writer.writeStartArray(); for(auto i = 0; i < 1000; ++i) { writer.writeStartObject(); writer.writeKey("key"); writer.writeStartObject(); for(auto j = 0; j < 1000; ++j) { writer.write(QString::number(j), j); } writer.writeEndObject(); writer.writeEndObject(); } writer.writeEndArray(); if(writer.end()) { stream.flush(); } else { qWarning() << "Error json"; } 

Tingkatan yang dicapai


Digunakan oleh QBENCHMARK selama rilis rilis. Fungsi diimplementasikan di kelas JsonWriterSaxTest .


SD OS 5.0 Juno, kernel 4.15.0-38-generik, cpu Intel® Core (TM) 2 Quad CPU 9550 @ 2.83GHz, RAM 4G, Qt 5.11.2 GCC 5.3.1


Array angka panjang


  • QJsonDocument: 42 msecs per iteration (total: 85, iterasi: 2)
  • JsonWriterSax: 23 msec per iterasi (total: 93, iterasi: 4)

Objek besar satu tingkat


  • QJsonDocument: 1.170 msecs per iteration (total: 1.170, iterasi: 1)
  • JsonWriterSax: 53 milidetik per iterasi (total: 53, iterasi: 1)

Dokumen rumit yang besar


  • QJsonDocument: 1.369 msec per iteration (total: 1.369, iterasi: 1)
  • JsonWriterSax: 463 msecs per iterasi (total: 463, iterasi: 1)

SD OS 5.0 Juno, kernel 4.15.0-38-generik, cpu Intel® Core (TM) i7-7500U CPU @ 2.70GHz, 8G RAM, Qt 5.11.2 GCC 5.3.1


Array angka panjang


  • QJsonDocument: 29,5 msecs per iterasi (total: 118, iterasi: 4)
  • JsonWriterSax: 13 msecs per iteration (total: 52, iterasi: 4)

Objek besar satu tingkat


  • QJsonDocument: 485 msecs per iteration (total: 485, iterasi: 1)
  • JsonWriterSax: 31 milidetik per iterasi (total: 62, iterasi: 2)

Dokumen rumit yang besar


  • QJsonDocument: 734 msecs per iteration (total: 734, iterasi: 1)
  • JsonWriterSax: 271 msecs per iterasi (total: 271, iterasi: 1)

MS Windows 7 SP1, CPU Intel® Core (TM) i7-4770 CPU @ 3.40GHz, 8G RAM, Qt 5.11.0 GCC 5.3.0


Array angka panjang


  • QJsonDocument: 669 msec per iteration (total: 669, iterasi: 1)
  • JsonWriterSax: 20 msec per iterasi (total: 81, iterasi: 4)

Objek besar satu tingkat


  • QJsonDocument: 1.568 msec per iteration (total: 1.568, iterasi: 1)
  • JsonWriterSax: 44 msecs per iterasi (total: 88, iterasi: 2)

Dokumen rumit yang besar


  • QJsonDocument: 1.167 msec per iteration (total: 1.167, iterasi: 1)
  • JsonWriterSax: 375 msecs per iterasi (total: 375, iterasi: 1)

MS Windows 7 SP1, CPU Intel® Core (TM) i3-3220 CPU @ 3.30GHz, 8G RAM, Qt 5.11.0 GCC 5.3.0


Array angka panjang


  • QJsonDocument: 772 msecs per iterasi (total: 772, iterasi: 1)
  • JsonWriterSax: 26 msecs per iterasi (total: 52, iterasi: 2)

Objek besar satu tingkat


  • QJsonDocument: 2.029 msecs per iteration (total: 2.029, iterasi: 1)
  • JsonWriterSax: 59 msecs per iteration (total: 59, iterasi: 1)

Dokumen rumit yang besar


  • QJsonDocument: 1.530 msec per iteration (total: 1.530, iterasi: 1)
  • JsonWriterSax: 495 msecs per iterasi (total: 495, iterasi: 1)

Prospek


Dalam versi mendatang saya berencana untuk menambahkan kemampuan untuk menggambarkan format data pengguna melalui fungsi lambda menggunakan QVariant, menambahkan kemampuan untuk menggunakan pemisah untuk memformat dokumen (dokumen cantik) dan, jika komunitas tertarik, saya dapat menambahkan parser SAX.


Omong-omong, perpustakaan saya membantu saya menemukan kesalahan overflow, yang memungkinkan qInfo (), qDebug (), qWarning () untuk mengatur format dan output dalam gaya modul logging Python. Saya juga berencana memposting perpustakaan ini di opensource - jika ada yang tertarik - tulis di komentar.

Source: https://habr.com/ru/post/id427899/


All Articles