json.cpp
Go to the documentation of this file.
1 /* Copyright 2011 Eeli Reilin. All rights reserved.
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5  *
6  * 1. Redistributions of source code must retain the above copyright notice,
7  * this list of conditions and the following disclaimer.
8  *
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
16  * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
18  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
19  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
20  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
22  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation
25  * are those of the authors and should not be interpreted as representing
26  * official policies, either expressed or implied, of Eeli Reilin.
27  */
28 
33 #include "json.h"
34 #include <iostream>
35 
36 namespace QtJson
37 {
38 
39 
40 static QString sanitizeString(QString str)
41 {
42  str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
43  str.replace(QLatin1String("\""), QLatin1String("\\\""));
44  str.replace(QLatin1String("\b"), QLatin1String("\\b"));
45  str.replace(QLatin1String("\f"), QLatin1String("\\f"));
46  str.replace(QLatin1String("\n"), QLatin1String("\\n"));
47  str.replace(QLatin1String("\r"), QLatin1String("\\r"));
48  str.replace(QLatin1String("\t"), QLatin1String("\\t"));
49  return QString(QLatin1String("\"%1\"")).arg(str);
50 }
51 
52 static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep)
53 {
54  QByteArray res;
55  Q_FOREACH(const QByteArray &i, list)
56  {
57  if(!res.isEmpty())
58  {
59  res += sep;
60  }
61  res += i;
62  }
63  return res;
64 }
65 
69 QVariant Json::parse(const QString &json)
70 {
71  bool success = true;
72  return Json::parse(json, success);
73 }
74 
78 QVariant Json::parse(const QString &json, bool &success)
79 {
80  success = true;
81 
82  //Return an empty QVariant if the JSON data is either null or empty
83  if(!json.isNull() || !json.isEmpty())
84  {
85  QString data = json;
86  //We'll start from index 0
87  int index = 0;
88 
89  //Parse the first value
90  QVariant value = Json::parseValue(data, index, success);
91 
92  //Return the parsed value
93  return value;
94  }
95  else
96  {
97  //Return the empty QVariant
98  return QVariant();
99  }
100 }
101 
102 QByteArray Json::serialize(const QVariant &data)
103 {
104  bool success = true;
105  return Json::serialize(data, success);
106 }
107 
108 QByteArray Json::serialize(const QVariant &data, bool &success)
109 {
110  QByteArray str;
111  success = true;
112 
113  if(!data.isValid()) // invalid or null?
114  {
115  str = "null";
116  }
117  else if((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list?
118  {
119  QList<QByteArray> values;
120  const QVariantList list = data.toList();
121  Q_FOREACH(const QVariant& v, list)
122  {
123  QByteArray serializedValue = serialize(v);
124  if(serializedValue.isNull())
125  {
126  success = false;
127  break;
128  }
129  values << serializedValue;
130  }
131 
132  str = "[ " + join( values, ", " ) + " ]";
133  }
134  else if(data.type() == QVariant::Map) // variant is a map?
135  {
136  const QVariantMap vmap = data.toMap();
137  QMapIterator<QString, QVariant> it( vmap );
138  str = "{ ";
139  QList<QByteArray> pairs;
140  while(it.hasNext())
141  {
142  it.next();
143  QByteArray serializedValue = serialize(it.value());
144  if(serializedValue.isNull())
145  {
146  success = false;
147  break;
148  }
149  pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue;
150  }
151  str += join(pairs, ", ");
152  str += " }";
153  }
154  else if((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array?
155  {
156  str = sanitizeString(data.toString()).toUtf8();
157  }
158  else if(data.type() == QVariant::Double) // double?
159  {
160  str = QByteArray::number(data.toDouble(), 'g', 20);
161  if(!str.contains(".") && ! str.contains("e"))
162  {
163  str += ".0";
164  }
165  }
166  else if (data.type() == QVariant::Bool) // boolean value?
167  {
168  str = data.toBool() ? "true" : "false";
169  }
170  else if (data.type() == QVariant::ULongLong) // large unsigned number?
171  {
172  str = QByteArray::number(data.value<qulonglong>());
173  }
174  else if ( data.canConvert<qlonglong>() ) // any signed number?
175  {
176  str = QByteArray::number(data.value<qlonglong>());
177  }
178  else if (data.canConvert<long>())
179  {
180  str = QString::number(data.value<long>()).toUtf8();
181  }
182  else if (data.canConvert<QString>()) // can value be converted to string?
183  {
184  // this will catch QDate, QDateTime, QUrl, ...
185  str = sanitizeString(data.toString()).toUtf8();
186  }
187  else
188  {
189  success = false;
190  }
191  if (success)
192  {
193  return str;
194  }
195  else
196  {
197  return QByteArray();
198  }
199 }
200 
204 QVariant Json::parseValue(const QString &json, int &index, bool &success)
205 {
206  //Determine what kind of data we should parse by
207  //checking out the upcoming token
208  switch(Json::lookAhead(json, index))
209  {
210  case JsonTokenString:
211  return Json::parseString(json, index, success);
212  case JsonTokenNumber:
213  return Json::parseNumber(json, index);
214  case JsonTokenCurlyOpen:
215  return Json::parseObject(json, index, success);
216  case JsonTokenSquaredOpen:
217  return Json::parseArray(json, index, success);
218  case JsonTokenTrue:
219  Json::nextToken(json, index);
220  return QVariant(true);
221  case JsonTokenFalse:
222  Json::nextToken(json, index);
223  return QVariant(false);
224  case JsonTokenNull:
225  Json::nextToken(json, index);
226  return QVariant();
227  case JsonTokenNone:
228  break;
229  }
230 
231  //If there were no tokens, flag the failure and return an empty QVariant
232  success = false;
233  return QVariant();
234 }
235 
239 QVariant Json::parseObject(const QString &json, int &index, bool &success)
240 {
241  QVariantMap map;
242  int token;
243 
244  //Get rid of the whitespace and increment index
245  Json::nextToken(json, index);
246 
247  //Loop through all of the key/value pairs of the object
248  bool done = false;
249  while(!done)
250  {
251  //Get the upcoming token
252  token = Json::lookAhead(json, index);
253 
254  if(token == JsonTokenNone)
255  {
256  success = false;
257  return QVariantMap();
258  }
259  else if(token == JsonTokenComma)
260  {
261  Json::nextToken(json, index);
262  }
263  else if(token == JsonTokenCurlyClose)
264  {
265  Json::nextToken(json, index);
266  return map;
267  }
268  else
269  {
270  //Parse the key/value pair's name
271  QString name = Json::parseString(json, index, success).toString();
272 
273  if(!success)
274  {
275  return QVariantMap();
276  }
277 
278  //Get the next token
279  token = Json::nextToken(json, index);
280 
281  //If the next token is not a colon, flag the failure
282  //return an empty QVariant
283  if(token != JsonTokenColon)
284  {
285  success = false;
286  return QVariant(QVariantMap());
287  }
288 
289  //Parse the key/value pair's value
290  QVariant value = Json::parseValue(json, index, success);
291 
292  if(!success)
293  {
294  return QVariantMap();
295  }
296 
297  //Assign the value to the key in the map
298  map[name] = value;
299  }
300  }
301 
302  //Return the map successfully
303  return QVariant(map);
304 }
305 
309 QVariant Json::parseArray(const QString &json, int &index, bool &success)
310 {
311  QVariantList list;
312 
313  Json::nextToken(json, index);
314 
315  bool done = false;
316  while(!done)
317  {
318  int token = Json::lookAhead(json, index);
319 
320  if(token == JsonTokenNone)
321  {
322  success = false;
323  return QVariantList();
324  }
325  else if(token == JsonTokenComma)
326  {
327  Json::nextToken(json, index);
328  }
329  else if(token == JsonTokenSquaredClose)
330  {
331  Json::nextToken(json, index);
332  break;
333  }
334  else
335  {
336  QVariant value = Json::parseValue(json, index, success);
337 
338  if(!success)
339  {
340  return QVariantList();
341  }
342 
343  list.push_back(value);
344  }
345  }
346 
347  return QVariant(list);
348 }
349 
353 QVariant Json::parseString(const QString &json, int &index, bool &success)
354 {
355  QString s;
356  QChar c;
357 
358  Json::eatWhitespace(json, index);
359 
360  c = json[index++];
361 
362  bool complete = false;
363  while(!complete)
364  {
365  if(index == json.size())
366  {
367  break;
368  }
369 
370  c = json[index++];
371 
372  if(c == '\"')
373  {
374  complete = true;
375  break;
376  }
377  else if(c == '\\')
378  {
379  if(index == json.size())
380  {
381  break;
382  }
383 
384  c = json[index++];
385 
386  if(c == '\"')
387  {
388  s.append('\"');
389  }
390  else if(c == '\\')
391  {
392  s.append('\\');
393  }
394  else if(c == '/')
395  {
396  s.append('/');
397  }
398  else if(c == 'b')
399  {
400  s.append('\b');
401  }
402  else if(c == 'f')
403  {
404  s.append('\f');
405  }
406  else if(c == 'n')
407  {
408  s.append('\n');
409  }
410  else if(c == 'r')
411  {
412  s.append('\r');
413  }
414  else if(c == 't')
415  {
416  s.append('\t');
417  }
418  else if(c == 'u')
419  {
420  int remainingLength = json.size() - index;
421 
422  if(remainingLength >= 4)
423  {
424  QString unicodeStr = json.mid(index, 4);
425 
426  int symbol = unicodeStr.toInt(0, 16);
427 
428  s.append(QChar(symbol));
429 
430  index += 4;
431  }
432  else
433  {
434  break;
435  }
436  }
437  }
438  else
439  {
440  s.append(c);
441  }
442  }
443 
444  if(!complete)
445  {
446  success = false;
447  return QVariant();
448  }
449 
450  s = QString::fromUtf8(s.toUtf8());
451 
452  return QVariant(s);
453 }
454 
458 QVariant Json::parseNumber(const QString &json, int &index)
459 {
460  Json::eatWhitespace(json, index);
461 
462  int lastIndex = Json::lastIndexOfNumber(json, index);
463  int charLength = (lastIndex - index) + 1;
464  QString numberStr;
465 
466  numberStr = json.mid(index, charLength);
467 
468  index = lastIndex + 1;
469 
470  if (numberStr.contains('.')) {
471  return QVariant(numberStr.toDouble(NULL));
472  } else if (numberStr.startsWith('-')) {
473  return QVariant(numberStr.toLongLong(NULL));
474  } else {
475  return QVariant(numberStr.toULongLong(NULL));
476  }
477 }
478 
482 int Json::lastIndexOfNumber(const QString &json, int index)
483 {
484  int lastIndex;
485 
486  for(lastIndex = index; lastIndex < json.size(); lastIndex++)
487  {
488  if(QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1)
489  {
490  break;
491  }
492  }
493 
494  return lastIndex -1;
495 }
496 
500 void Json::eatWhitespace(const QString &json, int &index)
501 {
502  for(; index < json.size(); index++)
503  {
504  if(QString(" \t\n\r").indexOf(json[index]) == -1)
505  {
506  break;
507  }
508  }
509 }
510 
514 int Json::lookAhead(const QString &json, int index)
515 {
516  int saveIndex = index;
517  return Json::nextToken(json, saveIndex);
518 }
519 
523 int Json::nextToken(const QString &json, int &index)
524 {
525  Json::eatWhitespace(json, index);
526 
527  if(index == json.size())
528  {
529  return JsonTokenNone;
530  }
531 
532  QChar c = json[index];
533  index++;
534  switch(c.toLatin1())
535  {
536  case '{': return JsonTokenCurlyOpen;
537  case '}': return JsonTokenCurlyClose;
538  case '[': return JsonTokenSquaredOpen;
539  case ']': return JsonTokenSquaredClose;
540  case ',': return JsonTokenComma;
541  case '"': return JsonTokenString;
542  case '0': case '1': case '2': case '3': case '4':
543  case '5': case '6': case '7': case '8': case '9':
544  case '-': return JsonTokenNumber;
545  case ':': return JsonTokenColon;
546  }
547 
548  index--;
549 
550  int remainingLength = json.size() - index;
551 
552  //True
553  if(remainingLength >= 4)
554  {
555  if (json[index] == 't' && json[index + 1] == 'r' &&
556  json[index + 2] == 'u' && json[index + 3] == 'e')
557  {
558  index += 4;
559  return JsonTokenTrue;
560  }
561  }
562 
563  //False
564  if (remainingLength >= 5)
565  {
566  if (json[index] == 'f' && json[index + 1] == 'a' &&
567  json[index + 2] == 'l' && json[index + 3] == 's' &&
568  json[index + 4] == 'e')
569  {
570  index += 5;
571  return JsonTokenFalse;
572  }
573  }
574 
575  //Null
576  if (remainingLength >= 4)
577  {
578  if (json[index] == 'n' && json[index + 1] == 'u' &&
579  json[index + 2] == 'l' && json[index + 3] == 'l')
580  {
581  index += 4;
582  return JsonTokenNull;
583  }
584  }
585 
586  return JsonTokenNone;
587 }
588 
589 
590 } //end namespace
static QVariant parse(const QString &json)
Definition: json.cpp:69
static QByteArray serialize(const QVariant &data)
Definition: json.cpp:102
Definition: json.cpp:36