Merge lp:~guillaume-chereau/stellarium/use-qt-json into lp:stellarium

Proposed by Guillaume Chereau
Status: Merged
Merged at revision: 8855
Proposed branch: lp:~guillaume-chereau/stellarium/use-qt-json
Merge into: lp:stellarium
Diff against target: 613 lines (+17/-519)
4 files modified
src/core/StelJsonParser.cpp (+15/-456)
src/core/StelJsonParser.hpp (+1/-35)
src/tests/testStelJsonParser.cpp (+1/-27)
src/tests/testStelJsonParser.hpp (+0/-1)
To merge this branch: bzr merge lp:~guillaume-chereau/stellarium/use-qt-json
Reviewer Review Type Date Requested Status
Alexander Wolf Approve
Review via email: mp+311241@code.launchpad.net

Description of the change

Use the Qt 5 Json parser instead of the hand-made one we used so far.

This should be OK since the compilation requires qt 5 now.

To post a comment you must log in.
Revision history for this message
Fabien Chéreau (xalioth) wrote :

did you try to build all plugins? Isn't anything using the iterator?

Revision history for this message
Guillaume Chereau (guillaume-chereau) wrote :

Yes, except the LogBook plugin that doesn't compile for other reasons.

Revision history for this message
Alexander Wolf (alexwolf) wrote :

It builds, but will not work without changes in plugins

Revision history for this message
Guillaume Chereau (guillaume-chereau) wrote :

>It builds, but will not work without changes in plugins
What plugin would be affected? I tried to keep it backward compatible
with the current API, except that I removed the iterator interface that
is not used anywhere as far as I can tell.

Revision history for this message
Alexander Wolf (alexwolf) wrote :

It's interesting. Current branch cannot parse pulsars.json catalog.

Revision history for this message
Alexander Wolf (alexwolf) wrote :

Current parser cannot understand digits with plus. Why?

Revision history for this message
Alexander Wolf (alexwolf) wrote :

I've updated pulsars.json

review: Approve
Revision history for this message
Alexander Wolf (alexwolf) wrote :

Maybe we should avoid using StelJsonParser and switch to use of QJsonDocument?

Revision history for this message
Fabien Chéreau (xalioth) wrote :

In a second step yes. I started to work on that once, but it's used in many
places.

On Fri, Nov 18, 2016 at 12:22 PM, Alexander Wolf <email address hidden>
wrote:

> Maybe we should avoid using StelJsonParser and switch to use of
> QJsonDocument?
> --
> https://code.launchpad.net/~guillaume-chereau/stellarium/
> use-qt-json/+merge/311241
> You are subscribed to branch lp:stellarium.
>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/core/StelJsonParser.cpp'
2--- src/core/StelJsonParser.cpp 2016-06-17 15:00:04 +0000
3+++ src/core/StelJsonParser.cpp 2016-11-18 09:14:03 +0000
4@@ -18,475 +18,34 @@
5
6 #include "StelJsonParser.hpp"
7 #include <QDebug>
8-#include <QBuffer>
9-#include <QDateTime>
10+#include <QJsonDocument>
11 #include <stdexcept>
12-#include <stdio.h>
13-
14-class StelJsonParserInstance
15-{
16-public:
17- StelJsonParserInstance(QIODevice* ain)
18- : input(ain)
19- , nextChar()
20- , hasNextChar(false)
21- {
22- cur = buffer;
23- last = cur;
24- }
25- inline void skipJson();
26- inline bool tryReadChar(char c);
27- inline bool skipAndConsumeChar(char r);
28- QByteArray readString();
29- QVariant readOther();
30- QVariant parse();
31-
32-private:
33- QIODevice* input;
34- char nextChar;
35- bool hasNextChar;
36-
37-#define BUFSIZESTATIC 65536
38- char buffer[BUFSIZESTATIC];
39- char* last;
40- char* cur;
41-
42- inline bool getNextFromBuffer(char *c)
43- {
44- if (cur==last)
45- {
46- if (cur==buffer-1) // End of file
47- return false;
48-
49- last = buffer + input->read(buffer, BUFSIZESTATIC);
50- if (last==buffer)
51- {
52- cur = buffer-1;
53- last = cur;
54- return false;
55- }
56- cur = buffer;
57- }
58- *c = *cur;
59- ++cur;
60- return true;
61- }
62-
63- inline bool getChar(char* c)
64- {
65- if (hasNextChar)
66- {
67- hasNextChar=false;
68- *c = nextChar;
69- return true;
70- }
71- return getNextFromBuffer(c);
72- }
73-
74- inline void ungetChar(char c)
75- {
76- nextChar=c;
77- hasNextChar=true;
78- }
79-
80- inline void skipLine()
81- {
82- if (hasNextChar && nextChar=='\n')
83- {
84- hasNextChar=false;
85- return;
86- }
87- hasNextChar=false;
88- char c;
89- while (getNextFromBuffer(&c) && c!='\n')
90- {}
91- }
92-
93- inline bool atEnd()
94- {
95- return cur==buffer-1;
96- }
97-};
98-
99-void StelJsonParserInstance::skipJson()
100-{
101- // There is a weakness in this code -- it will cause any standalone '/' to be absorbed.
102- char c;
103- while (getChar(&c))
104- {
105- switch (c)
106- {
107- case ' ':
108- case '\t':
109- case '\n':
110- case '\r':
111- break;
112- case '/':
113- {
114- if (!getChar(&c))
115- return;
116-
117- if (c=='/')
118- skipLine();
119- else
120- {
121- // We have a problem, we removed a '/'.. This should never happen with properly formatted JSON though
122- throw std::runtime_error(qPrintable(QString("Unexpected '/%1' in the JSON content").arg(c)));
123- }
124- }
125- break;
126- default:
127- ungetChar(c);
128- return;
129- }
130- }
131-}
132-
133-
134-bool StelJsonParserInstance::tryReadChar(char c)
135-{
136- char r;
137- if (!getChar(&r))
138- return false;
139-
140- if (r == c)
141- return true;
142-
143- ungetChar(r);
144- return false;
145-}
146-
147-bool StelJsonParserInstance::skipAndConsumeChar(char r)
148-{
149- char c;
150- while (getChar(&c))
151- {
152- switch (c)
153- {
154- case ' ':
155- case '\t':
156- case '\n':
157- case '\r':
158- break;
159- case '/':
160- {
161- if (!getChar(&c))
162- throw std::runtime_error(qPrintable(QString("Unexpected '/%1' in the JSON content").arg(c)));
163-
164- if (c=='/')
165- skipLine();
166- else
167- {
168- // We have a problem, we removed a '/'.. This should never happen with properly formatted JSON though
169- throw std::runtime_error(qPrintable(QString("Unexpected '/%1' in the JSON content").arg(c)));
170- }
171- }
172- break;
173- default:
174- if (r==c)
175- return true;
176- ungetChar(c);
177- return false;
178- }
179- }
180- return false;
181-}
182-
183-// Read a string without the initial "
184-QByteArray StelJsonParserInstance::readString()
185-{
186- QByteArray name;
187- char c;
188- while (getChar(&c))
189- {
190- switch (c)
191- {
192- case '"':
193- return name;
194- break;
195- case '\\':
196- {
197- bool gotChar=getChar(&c);
198- if (!gotChar) {qWarning() << "cannot read further, error?"; continue;}
199- if (c=='b') c='\b';
200- if (c=='f') c='\f';
201- if (c=='n') c='\n';
202- if (c=='r') c='\r';
203- if (c=='t') c='\t';
204- if (c=='u') {qWarning() << "don't support \\uxxxx char"; continue;}
205- }
206- break;
207- default:
208- name+=c;
209- break;
210- }
211- }
212- if (atEnd())
213- throw std::runtime_error(qPrintable(QString("End of file before end of string: "+name)));
214- throw std::runtime_error(qPrintable(QString("Read error before end of string: "+name)));
215-}
216-
217-QVariant StelJsonParserInstance::readOther()
218-{
219- QByteArray str;
220- char c;
221- while (getChar(&c))
222- {
223- if (c==' ' || c==',' || c=='\n' || c=='\r' || c==']' || c=='\t' || c=='}')
224- {
225- ungetChar(c);
226- break;
227- }
228- str+=c;
229- }
230- bool ok;
231- const int i = str.toInt(&ok);
232- if (ok)
233- return i;
234- const double d = str.toDouble(&ok);
235- if (ok)
236- return d;
237- if (str=="true")
238- return QVariant(true);
239- if (str=="false")
240- return QVariant(false);
241- if (str=="null")
242- return QVariant();
243- QDateTime dt = QDateTime::fromString(str, Qt::ISODate);
244- if (dt.isValid())
245- return QVariant(dt);
246-
247- throw std::runtime_error(qPrintable(QString("Invalid JSON value: \"")+str+"\""));
248-}
249-
250-// Parse the given input stream
251-QVariant StelJsonParserInstance::parse()
252-{
253- skipJson();
254-
255- char r;
256- if (!getChar(&r))
257- return QVariant();
258-
259- switch (r)
260- {
261- case '{':
262- {
263- // We've got an object (a tuple)
264- QVariantMap map;
265- if (skipAndConsumeChar('}'))
266- return map;
267- for (;;)
268- {
269- if (!skipAndConsumeChar('\"'))
270- {
271- char cc=0;
272- if (getChar(&cc))
273- throw std::runtime_error(qPrintable(QString("Expected '\"' at beginning of string, found: '%1' (ASCII %2)").arg(cc).arg((int)(cc))));
274- }
275- const QByteArray& ar = readString();
276- const QString& key = QString::fromUtf8(ar.constData(), ar.size());
277- if (!skipAndConsumeChar(':'))
278- throw std::runtime_error(qPrintable(QString("Expected ':' after a member name: ")+key));
279-
280- skipJson();
281- map.insert(key, parse());
282- if (!skipAndConsumeChar(','))
283- break;
284- }
285- if (!skipAndConsumeChar('}'))
286- throw std::runtime_error("Expected '}' to close an object");
287- return map;
288- }
289- case '[':
290- {
291- // We've got an array (a vector)
292- QVariantList list;
293- if (skipAndConsumeChar(']'))
294- return list;
295-
296- for (;;)
297- {
298- list.append(parse());
299- if (!skipAndConsumeChar(','))
300- break;
301- }
302-
303- if (!skipAndConsumeChar(']'))
304- throw std::runtime_error("Expected ']' to close an array");
305-
306- return list;
307- }
308- case '\"':
309- {
310- // We've got a string
311- const QByteArray& ar = readString();
312- return QString::fromUtf8(ar.constData(), ar.size());
313- }
314- default:
315- {
316- ungetChar(r);
317- return readOther();
318- }
319- }
320-}
321-
322-QHash<int, void (*)(const QVariant&, QIODevice*, int)> StelJsonParser::otherSerializer;
323-
324-// Serialize the passed QVariant as JSON into the output QIODevice
325+
326 void StelJsonParser::write(const QVariant& v, QIODevice* output, int indentLevel)
327 {
328- switch (v.type())
329- {
330- case QVariant::Bool:
331- output->write(v.toBool()==true ? "true" : "false");
332- break;
333- case QVariant::Invalid:
334- output->write("null");
335- break;
336- case QVariant::ByteArray:
337- {
338- QByteArray s(v.toByteArray());
339- s.replace('\\', "\\\\");
340- s.replace('\"', "\\\"");
341- s.replace('\b', "\\b");
342- s.replace('\n', "\\n");
343- s.replace('\f', "\\f");
344- s.replace('\r', "\\r");
345- s.replace('\t', "\\t");
346- output->write("\"" + s + "\"");
347- break;
348- }
349- case QVariant::String:
350- {
351- QString s(v.toString());
352- s.replace('\\', "\\\\");
353- s.replace('\"', "\\\"");
354- s.replace('\b', "\\b");
355- s.replace('\n', "\\n");
356- s.replace('\f', "\\f");
357- s.replace('\r', "\\r");
358- s.replace('\t', "\\t");
359- output->write(QString("\"%1\"").arg(s).toUtf8());
360- break;
361- }
362- case QVariant::Int:
363- case QVariant::Double:
364- output->write(v.toString().toUtf8());
365- break;
366- case QVariant::DateTime:
367- {
368- output->write(v.toDateTime().toString(Qt::ISODate).toUtf8());
369- break;
370- }
371- case QVariant::List:
372- {
373- output->putChar('[');
374- const QVariantList& l = v.toList();
375- for (int i=0;i<l.size();++i)
376- {
377- // Break line if we start an JSON Object for nice looking
378- if (l.at(i).type()==QVariant::Map)
379- output->putChar('\n');
380- write(l.at(i), output, indentLevel);
381- if (i!=l.size()-1)
382- output->write(", ");
383- }
384- output->putChar(']');
385- break;
386- }
387- case QVariant::Map:
388- {
389- const QByteArray prepend(indentLevel, '\t');
390- output->write(prepend);
391- output->write("{\n");
392- ++indentLevel;
393- const QVariantMap& m = v.toMap();
394- int j =0;
395- for (QVariantMap::ConstIterator i=m.constBegin();i!=m.constEnd();++i)
396- {
397- output->write(prepend);
398- output->write("\t\"");
399- output->write(i.key().toUtf8());
400- output->write("\": ");
401- // Break line if we start an JSON Object for nice looking
402- if (i.value().type()==QVariant::Map)
403- output->putChar('\n');
404- write(i.value(), output, indentLevel);
405- if (++j!=m.size())
406- output->putChar(',');
407- output->putChar('\n');
408- }
409- output->write(prepend);
410- output->write("}");
411- --indentLevel;
412- break;
413- }
414- default:
415- QHash<int, void (*)(const QVariant&, QIODevice*, int)>::ConstIterator iter = otherSerializer.find(v.userType());
416- if (iter!=otherSerializer.constEnd())
417- {
418- iter.value()(v, output, indentLevel);
419- }
420- else
421- output->write("null");
422- //qDebug() << v.type() << v.userType() << v.typeName();
423- //qWarning() << "Cannot serialize QVariant of type " << v.typeName() << " in JSON";
424- break;
425- }
426+ QByteArray json = write(v, indentLevel);
427+ output->write(json);
428 }
429
430 QByteArray StelJsonParser::write(const QVariant& jsonObject, int indentLevel)
431 {
432- QByteArray ar;
433- QBuffer buf(&ar);
434- buf.open(QIODevice::WriteOnly);
435- StelJsonParser::write(jsonObject, &buf, indentLevel);
436- buf.close();
437- return ar;
438+ QJsonDocument doc = QJsonDocument::fromVariant(jsonObject);
439+ return doc.toJson();
440 }
441
442 QVariant StelJsonParser::parse(QIODevice* input)
443 {
444- StelJsonParserInstance parser(input);
445- return parser.parse();
446+ QByteArray data = input->readAll();
447+ return parse(data);
448 }
449
450 QVariant StelJsonParser::parse(const QByteArray& aar)
451 {
452- QByteArray ar = aar;
453- QBuffer buf(&ar);
454- buf.open(QIODevice::ReadOnly);
455- QVariant v = StelJsonParser::parse(&buf);
456- buf.close();
457- return v;
458-}
459-
460-JsonListIterator::JsonListIterator(QIODevice* input)
461-{
462- parser = new StelJsonParserInstance(input);
463- if (!parser->skipAndConsumeChar('['))
464- {
465- throw std::runtime_error("Expected '[' to start a list iterator");
466- }
467- ahasNext = !parser->skipAndConsumeChar(']');
468-}
469-
470-JsonListIterator::~JsonListIterator()
471-{
472- Q_ASSERT(parser);
473- delete parser;
474- parser=NULL;
475-}
476-
477-QVariant JsonListIterator::next()
478-{
479- QVariant ret = parser->parse();
480- ahasNext = parser->skipAndConsumeChar(',');
481- if (!ahasNext)
482- {
483- if (!parser->skipAndConsumeChar(']'))
484- throw std::runtime_error("Expected ']' to end a list iterator");
485- }
486- return ret;
487+ QJsonParseError error;
488+ QJsonDocument doc = QJsonDocument::fromJson(aar, &error);
489+ if (error.error != QJsonParseError::NoError)
490+ {
491+ throw std::runtime_error(error.errorString().toLatin1().constData());
492+ }
493+ return doc.toVariant();
494 }
495
496=== modified file 'src/core/StelJsonParser.hpp'
497--- src/core/StelJsonParser.hpp 2012-01-11 10:50:37 +0000
498+++ src/core/StelJsonParser.hpp 2016-11-18 09:14:03 +0000
499@@ -24,34 +24,6 @@
500 #include <QByteArray>
501
502
503-//! Qt-style iterator over a JSON array. An actual list is not kept in memory,
504-//! so only forward iteration is supported and all methods, including the constructor,
505-//! involve read() calls on the QIODevice. Because of this, do not modify the
506-//! QIODevice between calls to JsonListIterator methods. Also, the toFront()
507-//! method has a special function and reset() is provided for convenience. Only
508-//! peekNext() is guaranteed not to modify the QIODevice.
509-class JsonListIterator
510-{
511-public:
512- //! Sets up JsonListIterator to read an array. Swallows all whitespace
513- //! up to a beginning '[' character. If '[' is not the first non-whitespace
514- //! character encountered, reset() is called and an exception is thrown.
515- JsonListIterator(QIODevice* input);
516- ~JsonListIterator();
517-
518- //! Reads and parses the next object from input. Advances QIODevice to
519- //! just after the object.
520- //! @return the next object from the array
521- QVariant next();
522-
523- //! Returns true if the next non-whitespace character is not a ']' character.
524- bool hasNext() const {return ahasNext;}
525-
526-private:
527- bool ahasNext;
528- class StelJsonParserInstance* parser;
529-};
530-
531 //! @class StelJsonParser
532 //! Qt-based simple JSON reader inspired by the one from <a href='http://zoolib.sourceforge.net/'>Zoolib</a>.
533 //! JSON is JavaScript Object Notation. See http://www.json.org/
534@@ -69,9 +41,6 @@
535 class StelJsonParser
536 {
537 public:
538- //! Create a JsonListIterator from the given input device.
539- static JsonListIterator initListIterator(QIODevice* in) {return JsonListIterator(in);}
540-
541 //! Parse the given input stream.
542 static QVariant parse(QIODevice* input);
543 static QVariant parse(const QByteArray& input);
544@@ -82,10 +51,7 @@
545 //! Serialize the passed QVariant as JSON in a QByteArray.
546 static QByteArray write(const QVariant& jsonObject, int indentLevel=0);
547
548- static void registerSerializerForType(int t, void (*func)(const QVariant&, QIODevice*, int)) {otherSerializer.insert(t, func);}
549-
550-private:
551- static QHash<int, void (*)(const QVariant&, QIODevice*, int)> otherSerializer;
552+ // static void registerSerializerForType(int t, void (*func)(const QVariant&, QIODevice*, int)) {otherSerializer.insert(t, func);}
553 };
554
555 #endif // _STELJSONPARSER_HPP_
556
557=== modified file 'src/tests/testStelJsonParser.cpp'
558--- src/tests/testStelJsonParser.cpp 2016-08-14 09:10:10 +0000
559+++ src/tests/testStelJsonParser.cpp 2016-11-18 09:14:03 +0000
560@@ -86,33 +86,6 @@
561 QVERIFY(ok==true);
562 }
563
564-void TestStelJsonParser::testIterator()
565-{
566- QBuffer buf;
567- buf.setData(listJsonBuff);
568- buf.open(QIODevice::ReadOnly);
569-
570- try
571- {
572- int tot = 0;
573- JsonListIterator iter = StelJsonParser::initListIterator(&buf);
574- while (iter.hasNext())
575- {
576- QVariant v = iter.next();
577- QVERIFY(v.canConvert<QVariantMap>());
578- ++tot;
579- }
580- QVERIFY(tot==3);
581- }
582- catch (std::runtime_error& e)
583- {
584- QString msg("Exception while loading JSON: ");
585- msg+=e.what();
586- QFAIL(qPrintable(msg));
587- }
588- buf.close();
589-}
590-
591 void TestStelJsonParser::testErrors()
592 {
593 bool wasCatched = false;
594@@ -139,6 +112,7 @@
595 buf.open(QIODevice::ReadOnly);
596 QVariant result;
597 QBENCHMARK {
598+ buf.seek(0);
599 result = StelJsonParser::parse(&buf);
600 }
601 }
602
603=== modified file 'src/tests/testStelJsonParser.hpp'
604--- src/tests/testStelJsonParser.hpp 2013-09-24 05:34:35 +0000
605+++ src/tests/testStelJsonParser.hpp 2016-11-18 09:14:03 +0000
606@@ -29,7 +29,6 @@
607 private slots:
608 void initTestCase();
609 void testBase();
610- void testIterator();
611 void benchmarkParse();
612 void testErrors();
613 private: