JsonWriterSax - مكتبة لإنشاء JSON

منذ بعض الوقت كتبت تطبيق c ++ / Qt الذي أرسل كميات كبيرة من البيانات بتنسيق JSON عبر الشبكة. تم استخدام QJsonDocument القياسي. أثناء التنفيذ ، واجهت أداءً ضعيفًا ، بالإضافة إلى تصميم فئة غير مريح ، والذي لم يسمح بالكشف العادي عن الأخطاء أثناء التشغيل. كانت النتيجة مكتبة JsonWriterSax ، والتي تسمح لك بكتابة مستندات JSON بأسلوب SAX بسرعة عالية ، والتي أنشرها على github.com بموجب ترخيص MIT. من يهتم - أطلب قطة.


جزء من النظرية


JSON (JavaScript Object Notation) هو تنسيق بيانات نصية تم تطويره بواسطة Douglas Crockford ومجموعة فرعية من لغة ECMAScript (JavaScript و JScript وما إلى ذلك تم إنشاؤها على أساسها). يستبدل JSON XML ، ويوسع قدرات التعشيش ويضيف أنواع البيانات. يتم استخدامه بنشاط على الإنترنت.


ولكن هناك عيوب في JSON. في رأيي ، من بين الأنواع القياسية ، من الواضح أنه لا يوجد نوع DateTime كافٍ - يجب عليك نقل القيمة في شكل رقم أو سلسلة ، وعند التحليل ، من الضروري اتخاذ قرار اعتمادًا على السياق. ولكن من الجدير بالذكر أنه في ECMAScript ، تم إنشاء نوع التاريخ منذ فترة طويلة ، ولم يتم التفكير فيه ، وفي عالم js ، يتم استخدام مكتبات الطرف الثالث للعمل مع التواريخ.


هناك طريقتان رئيسيتان للتحليل وإنشاء مستندات منظمة - SAX و DOM. ظهرت لـ XML ، ولكن يمكن استخدامها كنماذج لإنشاء معالجات تنسيقات أخرى.


SAX (واجهة برمجة تطبيقات بسيطة لـ XML)


يتم استخدامه لمعالجة البيانات التسلسلية ويسمح لك بمعالجة المستندات الكبيرة في دفق. عند القراءة ، تعود إلى معلومات التطبيق حول العنصر أو الخطأ الذي تم العثور عليه ، ولكن الحفاظ على المعلومات والتحكم في التعشيش يقع على عاتق التطبيق نفسه. عند التسجيل ، عادةً ما تتم الإشارة إلى خطوات النمط: بدء عنصر ، بدء عنصر فرعي ، كتابة رقم ، كتابة سطر ، إغلاق عنصر فرعي ، إغلاق عنصر. تشمل العيوب حقيقة أن المبرمج مطالب بكتابة الشفرة بشكل أكثر شمولاً ، لفهم بنية المستند بشكل أفضل والافتقار إلى تحرير مستند موجود أو تقييده الشديد.


DOM (نموذج كائن المستند)


باستخدام هذه الطريقة ، يتم إنشاء شجرة مستندات في الذاكرة يمكن إجراء تسلسل لها وإلغاء تسلسلها وتعديلها. العيب الرئيسي هو ارتفاع استهلاك الذاكرة وزيادة وقت المعالجة. تحت غطاء المحرك ، عادة ما يتم استخدام معالج SAX.


مشاكل QJsonDocument


يستخدم QJsonDocument القياسي نهج DOM. عند إنشاء مستند ، تكون السرعة منخفضة - يمكنك رؤية المعايير في نهاية المقالة. لكن أكبر مشكلة بالنسبة لي كانت تصميم خطأ إرجاع الخطأ.


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; } } 

في هذا المثال ، في حالة عدم وجود ذاكرة كافية ، ستتم كتابة الرسالة إلى تدفق الخطأ.


QJson: Document too large to store in data structure
ولن يتم إضافة البيانات بعد ذلك. في حالة وجود مصفوفة ، يمكنك التحقق من الحالة


 ja.size() - 1 != i 

ولكن ماذا تفعل عند العمل مع شيء؟ التحقق باستمرار من إضافة مفتاح جديد؟ تحليل تسجيل الدخول بحثًا عن خطأ؟


المكتبة


تتيح لك مكتبة JsonWriterSax كتابة مستند JSON في QTextStream بنمط SAX وهي متاحة على github بموجب ترخيص MIT. يقع التحكم في الذاكرة مع التطبيق. تتحكم المكتبة في تكامل JSON - إذا تمت إضافة العنصر بشكل غير صحيح ، فسوف تُرجع وظيفة الكتابة خطأ. للتحكم ، يتم استخدام قواعد KS. تم كتابة الاختبارات ، ولكن ربما تم ترك بعض الحالات دون مراقبة. إذا قام شخص ما بإصلاح التشغيل غير الصحيح للفحص والتقارير لتصحيح الخطأ - سأكون ممتنًا جدًا.


أعتقد أن أفضل وصف لمكتبة للمبرمج هو مثال الكود =)


أمثلة


إنشاء الصفيف


 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"; } 

نتيجة لذلك ، نحصل


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

إنشاء كائن


 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"; } 

نتيجة لذلك ، نحصل


 {"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"} 

إنشاء مستند يحتوي على تداخل وأنواع مختلفة


 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"; } 

المعايير


مستخدم بواسطة QBENCHMARK أثناء بناء الإصدار. يتم تنفيذ الوظائف في فئة JsonWriterSaxTest .


الابتدائية OS 5.0 Juno ، kernel 4.15.0-38-generic ، CPU Intel® Core (TM) 2 Quad CPU 9550 @ 2.83GHz ، 4G RAM ، Qt 5.11.2 GCC 5.3.1


صفيف رقم طويل


  • QJsonDocument: 42 ميللي ثانية لكل تكرار (المجموع: 85 ، التكرارات: 2)
  • JsonWriterSax: 23 ميللي ثانية لكل تكرار (المجموع: 93 ، التكرارات: 4)

كائن كبير على مستوى واحد


  • QJsonDocument: 1،170 ميللي ثانية لكل تكرار (المجموع: 1،170 ، التكرارات: 1)
  • JsonWriterSax: 53 ميللي ثانية لكل تكرار (المجموع: 53 ، التكرارات: 1)

وثيقة معقدة كبيرة


  • QJsonDocument: 1،369 مللي ثانية لكل تكرار (الإجمالي: 1،369 ، التكرارات: 1)
  • JsonWriterSax: 463 مللي ثانية لكل تكرار (الإجمالي: 463 ، التكرارات: 1)

الابتدائية OS 5.0 Juno ، kernel 4.15.0-38-generic ، وحدة المعالجة المركزية Intel® Core (TM) i7-7500U CPU @ 2.70GHz ، 8G RAM ، Qt 5.11.2 GCC 5.3.1


صفيف رقم طويل


  • QJsonDocument: 29.5 ميللي ثانية لكل تكرار (المجموع: 118 ، التكرارات: 4)
  • JsonWriterSax: 13 مللي ثانية لكل تكرار (الإجمالي: 52 ، التكرارات: 4)

كائن كبير على مستوى واحد


  • QJsonDocument: 485 ميللي ثانية لكل تكرار (الإجمالي: 485 ، التكرارات: 1)
  • JsonWriterSax: 31 مللي ثانية لكل تكرار (المجموع: 62 ، التكرارات: 2)

وثيقة معقدة كبيرة


  • QJsonDocument: 734 ميللي ثانية لكل تكرار (المجموع: 734 ، التكرارات: 1)
  • JsonWriterSax: 271 مللي ثانية لكل تكرار (الإجمالي: 271 ، التكرارات: 1)

MS Windows 7 SP1 ، وحدة المعالجة المركزية Intel® Core (TM) i7-4770 CPU @ 3.40GHz ، 8G RAM ، Qt 5.11.0 GCC 5.3.0


صفيف رقم طويل


  • QJsonDocument: 669 ميللي ثانية لكل تكرار (المجموع: 669 ، التكرارات: 1)
  • JsonWriterSax: 20 ميللي ثانية لكل تكرار (المجموع: 81 ، التكرارات: 4)

كائن كبير على مستوى واحد


  • QJsonDocument: 1،568 ميللي ثانية لكل تكرار (الإجمالي: 1،568 ، التكرارات: 1)
  • JsonWriterSax: 44 ميللي ثانية لكل تكرار (المجموع: 88 ، التكرارات: 2)

وثيقة معقدة كبيرة


  • QJsonDocument: 1،167 ميللي ثانية لكل تكرار (الإجمالي: 1،167 ، التكرارات: 1)
  • JsonWriterSax: 375 ميللي ثانية لكل تكرار (المجموع: 375 ، التكرارات: 1)

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


صفيف رقم طويل


  • QJsonDocument: 772 ميللي ثانية لكل تكرار (المجموع: 772 ، التكرارات: 1)
  • JsonWriterSax: 26 مللي ثانية لكل تكرار (الإجمالي: 52 ، التكرارات: 2)

كائن كبير على مستوى واحد


  • QJsonDocument: 2.029 ميللي ثانية لكل تكرار (الإجمالي: 2.029 ، التكرارات: 1)
  • JsonWriterSax: 59 ميللي ثانية لكل تكرار (المجموع: 59 ، التكرارات: 1)

وثيقة معقدة كبيرة


  • QJsonDocument: 1،530 ميللي ثانية لكل تكرار (الإجمالي: 1،530 ثانية ، التكرارات: 1)
  • JsonWriterSax: 495 مللي ثانية لكل تكرار (الإجمالي: 495 ، التكرارات: 1)

الآفاق


في الإصدارات المستقبلية ، أخطط لإضافة القدرة على وصف تنسيق بيانات المستخدم من خلال وظائف لامدا باستخدام QVariant ، وإضافة القدرة على استخدام الفواصل لتنسيق المستند (مستند جميل) ، وإذا كان المجتمع مهتمًا ، يمكنني إضافة محلل SAX.


بالمناسبة ، ساعدت مكتبتي في العثور على خطأ تجاوز السعة ، والذي يسمح لـ qInfo () و qDebug () و qWarning () بتعيين التنسيق والإخراج في نمط وحدة تسجيل Python. أخطط أيضًا لنشر هذه المكتبة في مصدر مفتوح - إذا كان أي شخص مهتمًا - اكتب في التعليقات.

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


All Articles