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

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