1 /**
2  * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa.
3  * Copyright (C) 2011  Eeli Reilin
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * \file json.cpp
21  */
22 
23 #include <QDateTime>
24 #include <QStringList>
25 #include "json.h"
26 
27 namespace QtJson {
28     static QString dateFormat, dateTimeFormat;
29     static bool prettySerialize = false;
30 
31     static QString sanitizeString(QString str);
32     static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep);
33     static QVariant parseValue(const QString &json, int &index, bool &success);
34     static QVariant parseObject(const QString &json, int &index, bool &success);
35     static QVariant parseArray(const QString &json, int &index, bool &success);
36     static QVariant parseString(const QString &json, int &index, bool &success);
37     static QVariant parseNumber(const QString &json, int &index);
38     static int lastIndexOfNumber(const QString &json, int index);
39     static void eatWhitespace(const QString &json, int &index);
40     static int lookAhead(const QString &json, int index);
41     static int nextToken(const QString &json, int &index);
42 
43     template<typename T>
serializeMap(const T & map,bool & success,int _level=0)44     QByteArray serializeMap(const T &map, bool &success, int _level = 0) {
45         QByteArray newline;
46         QByteArray tabs;
47         QByteArray tabsFields;
48         if (prettySerialize && !map.isEmpty()) {
49             newline = "\n";
50             for (uint l=1; l<_level; l++) {
51                 tabs += "    ";
52             }
53             tabsFields = tabs + "    ";
54         }
55 
56         QByteArray str = "{" + newline;
57         QList<QByteArray> pairs;
58         for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) {
59             bool otherSuccess = true;
60             QByteArray serializedValue = serialize(it.value(), otherSuccess, _level);
61             if (serializedValue.isNull()) {
62                 success = false;
63                 break;
64             }
65             pairs << tabsFields + sanitizeString(it.key()).toUtf8() + ":" + (prettySerialize ? " " : "") + serializedValue;
66         }
67 
68         str += join(pairs, "," + newline) + newline;
69         str += tabs + "}";
70         return str;
71     }
72 
73     void insert(QVariant &v, const QString &key, const QVariant &value);
74     void append(QVariant &v, const QVariant &value);
75 
76     template<typename T>
cloneMap(QVariant & json,const T & map)77     void cloneMap(QVariant &json, const T &map) {
78     for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) {
79         insert(json, it.key(), (*it));
80     }
81     }
82 
83     template<typename T>
cloneList(QVariant & json,const T & list)84     void cloneList(QVariant &json, const T &list) {
85     for (typename T::const_iterator it = list.begin(), itend = list.end(); it != itend; ++it) {
86         append(json, (*it));
87     }
88     }
89 
90     /**
91      * parse
92      */
parse(const QString & json)93     QVariant parse(const QString &json) {
94         bool success = true;
95         return parse(json, success);
96     }
97 
98     /**
99      * parse
100      */
parse(const QString & json,bool & success)101     QVariant parse(const QString &json, bool &success) {
102         success = true;
103 
104         // Return an empty QVariant if the JSON data is either null or empty
105         if (!json.isNull() || !json.isEmpty()) {
106             QString data = json;
107             // We'll start from index 0
108             int index = 0;
109 
110             // Parse the first value
111             QVariant value = parseValue(data, index, success);
112 
113             // Return the parsed value
114             return value;
115         } else {
116             // Return the empty QVariant
117             return QVariant();
118         }
119     }
120 
121     /**
122      * clone
123      */
clone(const QVariant & data)124     QVariant clone(const QVariant &data) {
125     QVariant v;
126 
127     if (data.type() == QVariant::Map) {
128         cloneMap(v, data.toMap());
129     } else if (data.type() == QVariant::Hash) {
130         cloneMap(v, data.toHash());
131     } else if (data.type() == QVariant::List) {
132         cloneList(v, data.toList());
133     } else if (data.type() == QVariant::StringList) {
134         cloneList(v, data.toStringList());
135     } else {
136         v = QVariant(data);
137     }
138 
139     return v;
140     }
141 
142     /**
143      * insert value (map case)
144      */
insert(QVariant & v,const QString & key,const QVariant & value)145     void insert(QVariant &v, const QString &key, const QVariant &value) {
146     if (!v.canConvert<QVariantMap>()) v = QVariantMap();
147     QVariantMap *p = (QVariantMap *)v.data();
148     p->insert(key, clone(value));
149     }
150 
151     /**
152      * append value (list case)
153      */
append(QVariant & v,const QVariant & value)154     void append(QVariant &v, const QVariant &value) {
155     if (!v.canConvert<QVariantList>()) v = QVariantList();
156     QVariantList *p = (QVariantList *)v.data();
157     p->append(value);
158     }
159 
serialize(const QVariant & data)160     QByteArray serialize(const QVariant &data) {
161         bool success = true;
162         return serialize(data, success);
163     }
164 
serialize(const QVariant & data,bool & success,int _level)165     QByteArray serialize(const QVariant &data, bool &success, int _level /*= 0*/) {
166         QByteArray newline;
167         QByteArray tabs;
168         QByteArray tabsFields;
169         if (prettySerialize) {
170             newline = "\n";
171             for (uint l=0; l<_level; l++) {
172                 tabs += "    ";
173             }
174             tabsFields = tabs + "    ";
175         }
176 
177         QByteArray str;
178         success = true;
179 
180         if (!data.isValid()) { // invalid or null?
181             str = "null";
182         } else if ((data.type() == QVariant::List) ||
183                    (data.type() == QVariant::StringList)) { // variant is a list?
184             QList<QByteArray> values;
185             const QVariantList list = data.toList();
186             Q_FOREACH(const QVariant& v, list) {
187                 bool otherSuccess = true;
188                 QByteArray serializedValue = serialize(v, otherSuccess, _level+1);
189                 if (serializedValue.isNull()) {
190                     success = false;
191                     break;
192                 }
193                 values << tabsFields + serializedValue;
194             }
195 
196             if (!values.isEmpty()) {
197                 str = "[" + newline + join( values, "," + newline ) + newline + tabs + "]";
198             } else {
199                 str = "[]";
200             }
201         } else if (data.type() == QVariant::Hash) { // variant is a hash?
202             str = serializeMap<>(data.toHash(), success, _level+1);
203         } else if (data.type() == QVariant::Map) { // variant is a map?
204             str = serializeMap<>(data.toMap(), success, _level+1);
205         } else if ((data.type() == QVariant::String) ||
206                    (data.type() == QVariant::ByteArray)) {// a string or a byte array?
207             str = sanitizeString(data.toString()).toUtf8();
208         } else if (data.type() == QVariant::Double) { // double?
209             double value = data.toDouble(&success);
210             if (success) {
211                 str = QByteArray::number(value, 'g');
212                 if (!str.contains(".") && ! str.contains("e")) {
213                     str += ".0";
214                 }
215             }
216         } else if (data.type() == QVariant::Bool) { // boolean value?
217             str = data.toBool() ? "true" : "false";
218         } else if (data.type() == QVariant::ULongLong) { // large unsigned number?
219             str = QByteArray::number(data.value<qulonglong>());
220         } else if (data.canConvert<qlonglong>()) { // any signed number?
221             str = QByteArray::number(data.value<qlonglong>());
222         } else if (data.canConvert<long>()) { //TODO: this code is never executed because all smaller types can be converted to qlonglong
223             str = QString::number(data.value<long>()).toUtf8();
224         } else if (data.type() == QVariant::DateTime) { // datetime value?
225             str = sanitizeString(dateTimeFormat.isEmpty()
226                                  ? data.toDateTime().toString()
227                                  : data.toDateTime().toString(dateTimeFormat)).toUtf8();
228         } else if (data.type() == QVariant::Date) { // date value?
229             str = sanitizeString(dateTimeFormat.isEmpty()
230                                  ? data.toDate().toString()
231                                  : data.toDate().toString(dateFormat)).toUtf8();
232         } else if (data.canConvert<QString>()) { // can value be converted to string?
233             // this will catch QUrl, ... (all other types which can be converted to string)
234             str = sanitizeString(data.toString()).toUtf8();
235         } else {
236             success = false;
237         }
238 
239         if (success) {
240             return str;
241         }
242         return QByteArray();
243     }
244 
serializeStr(const QVariant & data)245     QString serializeStr(const QVariant &data) {
246         return QString::fromUtf8(serialize(data));
247     }
248 
serializeStr(const QVariant & data,bool & success)249     QString serializeStr(const QVariant &data, bool &success) {
250         return QString::fromUtf8(serialize(data, success));
251     }
252 
253 
254     /**
255      * \enum JsonToken
256      */
257     enum JsonToken {
258         JsonTokenNone = 0,
259         JsonTokenCurlyOpen = 1,
260         JsonTokenCurlyClose = 2,
261         JsonTokenSquaredOpen = 3,
262         JsonTokenSquaredClose = 4,
263         JsonTokenColon = 5,
264         JsonTokenComma = 6,
265         JsonTokenString = 7,
266         JsonTokenNumber = 8,
267         JsonTokenTrue = 9,
268         JsonTokenFalse = 10,
269         JsonTokenNull = 11
270     };
271 
sanitizeString(QString str)272     static QString sanitizeString(QString str) {
273         str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
274         str.replace(QLatin1String("\""), QLatin1String("\\\""));
275         str.replace(QLatin1String("\b"), QLatin1String("\\b"));
276         str.replace(QLatin1String("\f"), QLatin1String("\\f"));
277         str.replace(QLatin1String("\n"), QLatin1String("\\n"));
278         str.replace(QLatin1String("\r"), QLatin1String("\\r"));
279         str.replace(QLatin1String("\t"), QLatin1String("\\t"));
280         return QString(QLatin1String("\"%1\"")).arg(str);
281     }
282 
join(const QList<QByteArray> & list,const QByteArray & sep)283     static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep) {
284         QByteArray res;
285         Q_FOREACH(const QByteArray &i, list) {
286             if (!res.isEmpty()) {
287                 res += sep;
288             }
289             res += i;
290         }
291         return res;
292     }
293 
294     /**
295      * parseValue
296      */
parseValue(const QString & json,int & index,bool & success)297     static QVariant parseValue(const QString &json, int &index, bool &success) {
298         // Determine what kind of data we should parse by
299         // checking out the upcoming token
300         switch(lookAhead(json, index)) {
301             case JsonTokenString:
302                 return parseString(json, index, success);
303             case JsonTokenNumber:
304                 return parseNumber(json, index);
305             case JsonTokenCurlyOpen:
306                 return parseObject(json, index, success);
307             case JsonTokenSquaredOpen:
308                 return parseArray(json, index, success);
309             case JsonTokenTrue:
310                 nextToken(json, index);
311                 return QVariant(true);
312             case JsonTokenFalse:
313                 nextToken(json, index);
314                 return QVariant(false);
315             case JsonTokenNull:
316                 nextToken(json, index);
317                 return QVariant();
318             case JsonTokenNone:
319                 break;
320         }
321 
322         // If there were no tokens, flag the failure and return an empty QVariant
323         success = false;
324         return QVariant();
325     }
326 
327     /**
328      * parseObject
329      */
parseObject(const QString & json,int & index,bool & success)330     static QVariant parseObject(const QString &json, int &index, bool &success) {
331         QVariantMap map;
332         int token;
333 
334         // Get rid of the whitespace and increment index
335         nextToken(json, index);
336 
337         // Loop through all of the key/value pairs of the object
338         bool done = false;
339         while (!done) {
340             // Get the upcoming token
341             token = lookAhead(json, index);
342 
343             if (token == JsonTokenNone) {
344                 success = false;
345                 return QVariantMap();
346             } else if (token == JsonTokenComma) {
347                 nextToken(json, index);
348             } else if (token == JsonTokenCurlyClose) {
349                 nextToken(json, index);
350                 return map;
351             } else {
352                 // Parse the key/value pair's name
353                 QString name = parseString(json, index, success).toString();
354 
355                 if (!success) {
356                     return QVariantMap();
357                 }
358 
359                 // Get the next token
360                 token = nextToken(json, index);
361 
362                 // If the next token is not a colon, flag the failure
363                 // return an empty QVariant
364                 if (token != JsonTokenColon) {
365                     success = false;
366                     return QVariant(QVariantMap());
367                 }
368 
369                 // Parse the key/value pair's value
370                 QVariant value = parseValue(json, index, success);
371 
372                 if (!success) {
373                     return QVariantMap();
374                 }
375 
376                 // Assign the value to the key in the map
377                 map[name] = value;
378             }
379         }
380 
381         // Return the map successfully
382         return QVariant(map);
383     }
384 
385     /**
386      * parseArray
387      */
parseArray(const QString & json,int & index,bool & success)388     static QVariant parseArray(const QString &json, int &index, bool &success) {
389         QVariantList list;
390 
391         nextToken(json, index);
392 
393         bool done = false;
394         while(!done) {
395             int token = lookAhead(json, index);
396 
397             if (token == JsonTokenNone) {
398                 success = false;
399                 return QVariantList();
400             } else if (token == JsonTokenComma) {
401                 nextToken(json, index);
402             } else if (token == JsonTokenSquaredClose) {
403                 nextToken(json, index);
404                 break;
405             } else {
406                 QVariant value = parseValue(json, index, success);
407                 if (!success) {
408                     return QVariantList();
409                 }
410                 list.push_back(value);
411             }
412         }
413 
414         return QVariant(list);
415     }
416 
417     /**
418      * parseString
419      */
parseString(const QString & json,int & index,bool & success)420     static QVariant parseString(const QString &json, int &index, bool &success) {
421         QString s;
422         QChar c;
423 
424         eatWhitespace(json, index);
425 
426         c = json[index++];
427 
428         bool complete = false;
429         while(!complete) {
430             if (index == json.size()) {
431                 break;
432             }
433 
434             c = json[index++];
435 
436             if (c == '\"') {
437                 complete = true;
438                 break;
439             } else if (c == '\\') {
440                 if (index == json.size()) {
441                     break;
442                 }
443 
444                 c = json[index++];
445 
446                 if (c == '\"') {
447                     s.append('\"');
448                 } else if (c == '\\') {
449                     s.append('\\');
450                 } else if (c == '/') {
451                     s.append('/');
452                 } else if (c == 'b') {
453                     s.append('\b');
454                 } else if (c == 'f') {
455                     s.append('\f');
456                 } else if (c == 'n') {
457                     s.append('\n');
458                 } else if (c == 'r') {
459                     s.append('\r');
460                 } else if (c == 't') {
461                     s.append('\t');
462                 } else if (c == 'u') {
463                     int remainingLength = json.size() - index;
464                     if (remainingLength >= 4) {
465                         QString unicodeStr = json.mid(index, 4);
466 
467                         int symbol = unicodeStr.toInt(0, 16);
468 
469                         s.append(QChar(symbol));
470 
471                         index += 4;
472                     } else {
473                         break;
474                     }
475                 }
476             } else {
477                 s.append(c);
478             }
479         }
480 
481         if (!complete) {
482             success = false;
483             return QVariant();
484         }
485 
486         return QVariant(s);
487     }
488 
489     /**
490      * parseNumber
491      */
parseNumber(const QString & json,int & index)492     static QVariant parseNumber(const QString &json, int &index) {
493         eatWhitespace(json, index);
494 
495         int lastIndex = lastIndexOfNumber(json, index);
496         int charLength = (lastIndex - index) + 1;
497         QString numberStr;
498 
499         numberStr = json.mid(index, charLength);
500 
501         index = lastIndex + 1;
502         bool ok;
503 
504         if (numberStr.contains('.')) {
505             return QVariant(numberStr.toDouble(NULL));
506         } else if (numberStr.startsWith('-')) {
507             int i = numberStr.toInt(&ok);
508             if (!ok) {
509                 qlonglong ll = numberStr.toLongLong(&ok);
510                 return ok ? ll : QVariant(numberStr);
511             }
512             return i;
513         } else {
514             uint u = numberStr.toUInt(&ok);
515             if (!ok) {
516                 qulonglong ull = numberStr.toULongLong(&ok);
517                 return ok ? ull : QVariant(numberStr);
518             }
519             return u;
520         }
521     }
522 
523     /**
524      * lastIndexOfNumber
525      */
lastIndexOfNumber(const QString & json,int index)526     static int lastIndexOfNumber(const QString &json, int index) {
527         int lastIndex;
528 
529         for(lastIndex = index; lastIndex < json.size(); lastIndex++) {
530             if (QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) {
531                 break;
532             }
533         }
534 
535         return lastIndex -1;
536     }
537 
538     /**
539      * eatWhitespace
540      */
eatWhitespace(const QString & json,int & index)541     static void eatWhitespace(const QString &json, int &index) {
542         for(; index < json.size(); index++) {
543             if (QString(" \t\n\r").indexOf(json[index]) == -1) {
544                 break;
545             }
546         }
547     }
548 
549     /**
550      * lookAhead
551      */
lookAhead(const QString & json,int index)552     static int lookAhead(const QString &json, int index) {
553         int saveIndex = index;
554         return nextToken(json, saveIndex);
555     }
556 
557     /**
558      * nextToken
559      */
nextToken(const QString & json,int & index)560     static int nextToken(const QString &json, int &index) {
561         eatWhitespace(json, index);
562 
563         if (index == json.size()) {
564             return JsonTokenNone;
565         }
566 
567         QChar c = json[index];
568         index++;
569         switch(c.toLatin1()) {
570             case '{': return JsonTokenCurlyOpen;
571             case '}': return JsonTokenCurlyClose;
572             case '[': return JsonTokenSquaredOpen;
573             case ']': return JsonTokenSquaredClose;
574             case ',': return JsonTokenComma;
575             case '"': return JsonTokenString;
576             case '0': case '1': case '2': case '3': case '4':
577             case '5': case '6': case '7': case '8': case '9':
578             case '-': return JsonTokenNumber;
579             case ':': return JsonTokenColon;
580         }
581         index--; // ^ WTF?
582 
583         int remainingLength = json.size() - index;
584 
585         // True
586         if (remainingLength >= 4) {
587             if (json[index] == 't' && json[index + 1] == 'r' &&
588                 json[index + 2] == 'u' && json[index + 3] == 'e') {
589                 index += 4;
590                 return JsonTokenTrue;
591             }
592         }
593 
594         // False
595         if (remainingLength >= 5) {
596             if (json[index] == 'f' && json[index + 1] == 'a' &&
597                 json[index + 2] == 'l' && json[index + 3] == 's' &&
598                 json[index + 4] == 'e') {
599                 index += 5;
600                 return JsonTokenFalse;
601             }
602         }
603 
604         // Null
605         if (remainingLength >= 4) {
606             if (json[index] == 'n' && json[index + 1] == 'u' &&
607                 json[index + 2] == 'l' && json[index + 3] == 'l') {
608                 index += 4;
609                 return JsonTokenNull;
610             }
611         }
612 
613         return JsonTokenNone;
614     }
615 
setDateTimeFormat(const QString & format)616     void setDateTimeFormat(const QString &format) {
617         dateTimeFormat = format;
618     }
619 
setDateFormat(const QString & format)620     void setDateFormat(const QString &format) {
621         dateFormat = format;
622     }
623 
getDateTimeFormat()624     QString getDateTimeFormat() {
625         return dateTimeFormat;
626     }
627 
getDateFormat()628     QString getDateFormat() {
629         return dateFormat;
630     }
631 
setPrettySerialize(bool enabled)632     void setPrettySerialize(bool enabled) {
633         prettySerialize = enabled;
634     }
635 
isPrettySerialize()636     bool isPrettySerialize() {
637         return prettySerialize;
638     }
639 
640 
641 
642     QQueue<BuilderJsonObject *> BuilderJsonObject::created_list;
643 
BuilderJsonObject()644     BuilderJsonObject::BuilderJsonObject() {
645         // clean objects previous "created"
646         while (!BuilderJsonObject::created_list.isEmpty()) {
647             delete BuilderJsonObject::created_list.dequeue();
648         }
649     }
650 
BuilderJsonObject(JsonObject & json)651     BuilderJsonObject::BuilderJsonObject(JsonObject &json) {
652         BuilderJsonObject();
653 
654         obj = json;
655     }
656 
set(const QString & key,const QVariant & value)657     BuilderJsonObject *BuilderJsonObject::set(const QString &key, const QVariant &value) {
658         obj[key] = value;
659 
660         return this;
661     }
662 
set(const QString & key,BuilderJsonObject * builder)663     BuilderJsonObject *BuilderJsonObject::set(const QString &key, BuilderJsonObject *builder) {
664         return set(key, builder->create());
665     }
666 
set(const QString & key,BuilderJsonArray * builder)667     BuilderJsonObject *BuilderJsonObject::set(const QString &key, BuilderJsonArray *builder) {
668         return set(key, builder->create());
669     }
670 
create()671     JsonObject BuilderJsonObject::create() {
672         BuilderJsonObject::created_list.enqueue(this);
673 
674         return obj;
675     }
676 
677 
678     QQueue<BuilderJsonArray *> BuilderJsonArray::created_list;
679 
BuilderJsonArray()680     BuilderJsonArray::BuilderJsonArray() {
681         // clean objects previous "created"
682         while (!BuilderJsonArray::created_list.isEmpty()) {
683             delete BuilderJsonArray::created_list.dequeue();
684         }
685     }
686 
BuilderJsonArray(JsonArray & json)687     BuilderJsonArray::BuilderJsonArray(JsonArray &json) {
688         BuilderJsonArray();
689 
690         array = json;
691     }
692 
add(const QVariant & element)693     BuilderJsonArray *BuilderJsonArray::add(const QVariant &element) {
694         array.append(element);
695 
696         return this;
697     }
698 
add(BuilderJsonObject * builder)699     BuilderJsonArray *BuilderJsonArray::add(BuilderJsonObject *builder) {
700         return add(builder->create());
701     }
702 
add(BuilderJsonArray * builder)703     BuilderJsonArray *BuilderJsonArray::add(BuilderJsonArray *builder) {
704         return add(builder->create());
705     }
706 
create()707     JsonArray BuilderJsonArray::create() {
708         BuilderJsonArray::created_list.enqueue(this);
709 
710         return array;
711     }
712 
713 
714 
715 
objectBuilder()716     BuilderJsonObject *objectBuilder() {
717         return new BuilderJsonObject();
718     }
719 
objectBuilder(JsonObject & json)720     BuilderJsonObject *objectBuilder(JsonObject &json) {
721         return new BuilderJsonObject(json);
722     }
723 
arrayBuilder()724     BuilderJsonArray *arrayBuilder() {
725         return new BuilderJsonArray();
726     }
727 
arrayBuilder(JsonArray & json)728     BuilderJsonArray *arrayBuilder(JsonArray &json) {
729         return new BuilderJsonArray(json);
730     }
731 
732 } //end namespace
733 
734