QJson home page
serializer.cpp
1 /* This file is part of qjson
2  *
3  * Copyright (C) 2009 Till Adam <adam@kde.org>
4  * Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
5  * Copyright (C) 2016 Anton Kudryavtsev <a.kudryavtsev@netris.ru>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License version 2.1, as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "serializer.h"
23 
24 #include <QtCore/QDataStream>
25 #include <QtCore/QStringList>
26 #include <QtCore/QVariant>
27 
28 // cmath does #undef for isnan and isinf macroses what can be defined in math.h
29 #if defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS)
30 # include <math.h>
31 #else
32 # include <cmath>
33 #endif
34 
35 #ifdef Q_OS_SOLARIS
36 # ifndef isinf
37 # include <ieeefp.h>
38 # define isinf(x) (!finite((x)) && (x)==(x))
39 # endif
40 #endif
41 
42 #ifdef _MSC_VER // using MSVC compiler
43 #include <float.h>
44 #endif
45 
46 using namespace QJson;
47 
48 class Serializer::SerializerPrivate {
49  public:
50  SerializerPrivate() :
51  specialNumbersAllowed(false),
52  indentMode(QJson::IndentNone),
53  doublePrecision(6) {
54  errorMessage.clear();
55  }
56  QString errorMessage;
59  int doublePrecision;
60 
61  QByteArray serialize( const QVariant &v, bool *ok, int indentLevel = 0);
62 
63  static QByteArray buildIndent(int spaces);
64  static QByteArray escapeString( const QString& str );
65  static QByteArray join( const QList<QByteArray>& list, const QByteArray& sep );
66  static QByteArray join( const QList<QByteArray>& list, char sep );
67 };
68 
69 QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) {
70  QByteArray res;
71  Q_FOREACH( const QByteArray& i, list ) {
72  if ( !res.isEmpty() )
73  res += sep;
74  res += i;
75  }
76  return res;
77 }
78 
79 QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, char sep ) {
80  QByteArray res;
81  Q_FOREACH( const QByteArray& i, list ) {
82  if ( !res.isEmpty() )
83  res += sep;
84  res += i;
85  }
86  return res;
87 }
88 
89 QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, bool *ok, int indentLevel)
90 {
91  QByteArray str;
92  const QVariant::Type type = v.type();
93 
94  if ( ! v.isValid() ) { // invalid or null?
95  str = "null";
96  } else if (( type == QVariant::List ) || ( type == QVariant::StringList )) { // an array or a stringlist?
97  const QVariantList list = v.toList();
98  QList<QByteArray> values;
99  Q_FOREACH( const QVariant& var, list )
100  {
101  QByteArray serializedValue;
102 
103  serializedValue = serialize( var, ok, indentLevel+1);
104 
105  if ( !*ok ) {
106  break;
107  }
108  switch(indentMode) {
109  case QJson::IndentFull :
110  case QJson::IndentMedium :
111  case QJson::IndentMinimum :
112  values << serializedValue;
113  break;
114  case QJson::IndentCompact :
115  case QJson::IndentNone :
116  default:
117  values << serializedValue.trimmed();
118  break;
119  }
120  }
121 
122  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull ) {
123  QByteArray indent = buildIndent(indentLevel);
124  str = indent + "[\n" + join( values, ",\n" ) + '\n' + indent + ']';
125  }
126  else if (indentMode == QJson::IndentMinimum) {
127  QByteArray indent = buildIndent(indentLevel);
128  str = indent + "[\n" + join( values, ",\n" ) + '\n' + indent + ']';
129  }
130  else if (indentMode == QJson::IndentCompact) {
131  str = '[' + join( values, "," ) + ']';
132  }
133  else {
134  str = "[ " + join( values, ", " ) + " ]";
135  }
136 
137  } else if ( type == QVariant::Map ) { // variant is a map?
138  const QVariantMap vmap = v.toMap();
139 
140  if (indentMode == QJson::IndentMinimum) {
141  QByteArray indent = buildIndent(indentLevel);
142  str = indent + "{ ";
143  }
144  else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
145  QByteArray indent = buildIndent(indentLevel);
146  QByteArray nextindent = buildIndent(indentLevel + 1);
147  str = indent + "{\n" + nextindent;
148  }
149  else if (indentMode == QJson::IndentCompact) {
150  str = "{";
151  }
152  else {
153  str = "{ ";
154  }
155 
156  QList<QByteArray> pairs;
157  for (QVariantMap::const_iterator it = vmap.begin(), end = vmap.end(); it != end; ++it) {
158  indentLevel++;
159  QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
160  indentLevel--;
161  if ( !*ok ) {
162  break;
163  }
164  QByteArray key = escapeString( it.key() );
165  QByteArray value = serializedValue.trimmed();
166  if (indentMode == QJson::IndentCompact) {
167  pairs << key + ':' + value;
168  } else {
169  pairs << key + " : " + value;
170  }
171  }
172 
173  if (indentMode == QJson::IndentFull) {
174  QByteArray indent = buildIndent(indentLevel + 1);
175  str += join( pairs, ",\n" + indent);
176  }
177  else if (indentMode == QJson::IndentCompact) {
178  str += join( pairs, ',' );
179  }
180  else {
181  str += join( pairs, ", " );
182  }
183 
184  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
185  QByteArray indent = buildIndent(indentLevel);
186  str += '\n' + indent + '}';
187  }
188  else if (indentMode == QJson::IndentCompact) {
189  str += '}';
190  }
191  else {
192  str += " }";
193  }
194 
195  } else if ( type == QVariant::Hash ) { // variant is a hash?
196  const QVariantHash vhash = v.toHash();
197 
198  if (indentMode == QJson::IndentMinimum) {
199  QByteArray indent = buildIndent(indentLevel);
200  str = indent + "{ ";
201  }
202  else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
203  QByteArray indent = buildIndent(indentLevel);
204  QByteArray nextindent = buildIndent(indentLevel + 1);
205  str = indent + "{\n" + nextindent;
206  }
207  else if (indentMode == QJson::IndentCompact) {
208  str = "{";
209  }
210  else {
211  str = "{ ";
212  }
213 
214  QList<QByteArray> pairs;
215  for (QVariantHash::const_iterator it = vhash.begin(), end = vhash.end(); it != end; ++it) {
216  QByteArray serializedValue = serialize( it.value(), ok, indentLevel + 1);
217 
218  if ( !*ok ) {
219  break;
220  }
221  QByteArray key = escapeString( it.key() );
222  QByteArray value = serializedValue.trimmed();
223  if (indentMode == QJson::IndentCompact) {
224  pairs << key + ':' + value;
225  } else {
226  pairs << key + " : " + value;
227  }
228  }
229 
230  if (indentMode == QJson::IndentFull) {
231  QByteArray indent = buildIndent(indentLevel + 1);
232  str += join( pairs, ",\n" + indent);
233  }
234  else if (indentMode == QJson::IndentCompact) {
235  str += join( pairs, ',' );
236  }
237  else {
238  str += join( pairs, ", " );
239  }
240 
241  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
242  QByteArray indent = buildIndent(indentLevel);
243  str += '\n' + indent + '}';
244  }
245  else if (indentMode == QJson::IndentCompact) {
246  str += '}';
247  }
248  else {
249  str += " }";
250  }
251 
252  } else {
253  // Add indent, we may need to remove it later for some layouts
254  switch(indentMode) {
255  case QJson::IndentFull :
256  case QJson::IndentMedium :
257  case QJson::IndentMinimum :
258  str += buildIndent(indentLevel);
259  break;
260  case QJson::IndentCompact :
261  case QJson::IndentNone :
262  default:
263  break;
264  }
265 
266  if (( type == QVariant::String ) || ( type == QVariant::ByteArray )) { // a string or a byte array?
267  str += escapeString( v.toString() );
268  } else if (( type == QVariant::Double) || ((QMetaType::Type)type == QMetaType::Float)) { // a double or a float?
269  const double value = v.toDouble();
270  #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
271  const bool special = _isnan(value) || !_finite(value);
272  #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS)
273  const bool special = isnan(value) || isinf(value);
274  #else
275  const bool special = std::isnan(value) || std::isinf(value);
276  #endif
277  if (special) {
278  if (specialNumbersAllowed) {
279  #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
280  if (_isnan(value)) {
281  #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS)
282  if (isnan(value)) {
283  #else
284  if (std::isnan(value)) {
285  #endif
286  str += "NaN";
287  } else {
288  if (value<0) {
289  str += '-';
290  }
291  str += "Infinity";
292  }
293  } else {
294  errorMessage += QLatin1String("Attempt to write NaN or infinity, which is not supported by json\n");
295  *ok = false;
296  }
297  } else {
298  str = QByteArray::number( value , 'g', doublePrecision);
299  if( !str.contains( '.' ) && !str.contains( 'e' ) ) {
300  str += ".0";
301  }
302  }
303  } else if ( type == QVariant::Bool ) { // boolean value?
304  str += ( v.toBool() ? "true" : "false" );
305  } else if ( type == QVariant::ULongLong ) { // large unsigned number?
306  str += QByteArray::number( v.value<qulonglong>() );
307  } else if ( type == QVariant::UInt ) { // unsigned int number?
308  str += QByteArray::number( v.value<quint32>() );
309  } else if ( v.canConvert<qlonglong>() ) { // any signed number?
310  str += QByteArray::number( v.value<qlonglong>() );
311  } else if ( v.canConvert<int>() ) { // unsigned short number?
312  str += QByteArray::number( v.value<int>() );
313  } else if ( v.canConvert<QString>() ){ // can value be converted to string?
314  // this will catch QDate, QDateTime, QUrl, ...
315  str += escapeString( v.toString() );
316  //TODO: catch other values like QImage, QRect, ...
317  } else {
318  *ok = false;
319  errorMessage += QLatin1String("Cannot serialize ");
320  errorMessage += v.toString();
321  errorMessage += QLatin1String(" because type ");
322  errorMessage += QLatin1String(v.typeName());
323  errorMessage += QLatin1String(" is not supported by QJson\n");
324  }
325  }
326  if ( *ok )
327  {
328  return str;
329  }
330  else
331  return QByteArray();
332 }
333 
334 QByteArray Serializer::SerializerPrivate::buildIndent(int spaces)
335 {
336  QByteArray indent;
337  if (spaces < 0) {
338  spaces = 0;
339  }
340  for (int i = 0; i < spaces; i++ ) {
341  indent += ' ';
342  }
343  return indent;
344 }
345 
346 QByteArray Serializer::SerializerPrivate::escapeString( const QString& str )
347 {
348  QByteArray result;
349  result.reserve(str.size() + 2);
350  result.append('\"');
351  for (QString::const_iterator it = str.begin(), end = str.end(); it != end; ++it) {
352  ushort unicode = it->unicode();
353  switch ( unicode ) {
354  case '\"':
355  result.append("\\\"");
356  break;
357  case '\\':
358  result.append("\\\\");
359  break;
360  case '\b':
361  result.append("\\b");
362  break;
363  case '\f':
364  result.append("\\f");
365  break;
366  case '\n':
367  result.append("\\n");
368  break;
369  case '\r':
370  result.append("\\r");
371  break;
372  case '\t':
373  result.append("\\t");
374  break;
375  default:
376  if ( unicode > 0x1F && unicode < 128 ) {
377  result.append(static_cast<char>(unicode));
378  } else {
379  char escaped[7];
380  qsnprintf(escaped, sizeof(escaped)/sizeof(char), "\\u%04x", unicode);
381  result.append(escaped);
382  }
383  }
384  }
385  result.append('\"');
386  return result;
387 }
388 
389 Serializer::Serializer()
390  : d( new SerializerPrivate )
391 {
392 }
393 
394 Serializer::~Serializer() {
395  delete d;
396 }
397 
398 void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok)
399 {
400  Q_ASSERT( io );
401  *ok = true;
402 
403  if (!io->isOpen()) {
404  if (!io->open(QIODevice::WriteOnly)) {
405  d->errorMessage = QLatin1String("Error opening device");
406  *ok = false;
407  return;
408  }
409  }
410 
411  if (!io->isWritable()) {
412  d->errorMessage = QLatin1String("Device is not readable");
413  io->close();
414  *ok = false;
415  return;
416  }
417 
418  const QByteArray str = serialize( v, ok);
419  if (*ok && (io->write(str) != str.count())) {
420  *ok = false;
421  d->errorMessage = QLatin1String("Something went wrong while writing to IO device");
422  }
423 }
424 
425 QByteArray Serializer::serialize( const QVariant &v)
426 {
427  bool ok;
428 
429  return serialize(v, &ok);
430 }
431 
432 QByteArray Serializer::serialize( const QVariant &v, bool *ok)
433 {
434  bool _ok = true;
435  d->errorMessage.clear();
436 
437  if (ok) {
438  *ok = true;
439  } else {
440  ok = &_ok;
441  }
442 
443  return d->serialize(v, ok);
444 }
445 
447  d->specialNumbersAllowed = allow;
448 }
449 
451  return d->specialNumbersAllowed;
452 }
453 
455  d->indentMode = mode;
456 }
457 
459  d->doublePrecision = precision;
460 }
461 
463  return d->indentMode;
464 }
465 
467  return d->errorMessage;
468 }
469 
void setIndentMode(IndentMode mode=QJson::IndentNone)
Definition: serializer.cpp:454
IndentMode indentMode() const
Definition: serializer.cpp:462
bool specialNumbersAllowed() const
Definition: serializer.cpp:450
void setDoublePrecision(int precision)
Definition: serializer.cpp:458
void serialize(const QVariant &variant, QIODevice *out, bool *ok)
Definition: serializer.cpp:398
QString errorMessage() const
Definition: serializer.cpp:466
void allowSpecialNumbers(bool allow)
Definition: serializer.cpp:446
IndentMode
How the indentation should work.
Definition: serializer.h:103

SourceForge Logo hosts this site. Send comments to:
QJson Developers