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  *
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 #include <cmath>
29 
30 #ifdef _MSC_VER // using MSVC compiler
31 #include <float.h>
32 #endif
33 
34 using namespace QJson;
35 
36 class Serializer::SerializerPrivate {
37  public:
38  SerializerPrivate() :
39  specialNumbersAllowed(false),
40  indentMode(QJson::IndentNone),
41  doublePrecision(6) {
42  errorMessage.clear();
43  }
44  QString errorMessage;
47  int doublePrecision;
48  QByteArray buildIndent(int spaces);
49  QByteArray serialize( const QVariant &v, bool *ok, int indentLevel = 0);
50  QString sanitizeString( QString str );
51  QByteArray join( const QList<QByteArray>& list, const QByteArray& sep );
52 };
53 
54 QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) {
55  QByteArray res;
56  Q_FOREACH( const QByteArray& i, list ) {
57  if ( !res.isEmpty() )
58  res += sep;
59  res += i;
60  }
61  return res;
62 }
63 
64 QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, bool *ok, int indentLevel)
65 {
66  QByteArray str;
67  bool error = false;
68  QByteArray indent;
69 
70  if ( ! v.isValid() ) { // invalid or null?
71  str = "null";
72  } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
73  const QVariantList list = v.toList();
74  QList<QByteArray> values;
75  Q_FOREACH( const QVariant& var, list )
76  {
77  indentLevel++;
78  QByteArray serializedValue = serialize( var, ok, indentLevel);
79  indentLevel--;
80  if ( !*ok ) {
81  break;
82  }
83  values << serializedValue;
84  }
85 
86  if (indentMode == QJson::IndentMinimum) {
87  QByteArray indent = buildIndent(indentLevel - 1);
88  str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
89  }
90  else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
91  QByteArray indent = buildIndent(indentLevel);
92  str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
93  }
94  else if (indentMode == QJson::IndentCompact) {
95  str = "[" + join( values, "," ) + "]";
96  }
97  else {
98  str = "[ " + join( values, ", " ) + " ]";
99  }
100 
101  } else if ( v.type() == QVariant::Map ) { // variant is a map?
102  const QVariantMap vmap = v.toMap();
103  QMapIterator<QString, QVariant> it( vmap );
104 
105  if (indentMode == QJson::IndentMinimum) {
106  QByteArray indent = buildIndent(indentLevel);
107  str = indent + "{ ";
108  }
109  else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
110  QByteArray indent = buildIndent(indentLevel);
111  QByteArray nextindent = buildIndent(indentLevel + 1);
112  str = indent + "{\n" + nextindent;
113  }
114  else if (indentMode == QJson::IndentCompact) {
115  str = "{";
116  }
117  else {
118  str = "{ ";
119  }
120 
121  QList<QByteArray> pairs;
122  while ( it.hasNext() ) {
123  it.next();
124  indentLevel++;
125  QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
126  indentLevel--;
127  if ( !*ok ) {
128  break;
129  }
130  QByteArray key = sanitizeString( it.key() ).toUtf8();
131  QByteArray value = serializedValue;
132  if (indentMode == QJson::IndentCompact) {
133  pairs << key + ":" + value;
134  } else {
135  pairs << key + " : " + value;
136  }
137  }
138 
139  if (indentMode == QJson::IndentFull) {
140  QByteArray indent = buildIndent(indentLevel + 1);
141  str += join( pairs, ",\n" + indent);
142  }
143  else if (indentMode == QJson::IndentCompact) {
144  str += join( pairs, "," );
145  }
146  else {
147  str += join( pairs, ", " );
148  }
149 
150  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
151  QByteArray indent = buildIndent(indentLevel);
152  str += "\n" + indent + "}";
153  }
154  else if (indentMode == QJson::IndentCompact) {
155  str += "}";
156  }
157  else {
158  str += " }";
159  }
160 
161  } else if ( v.type() == QVariant::Hash ) { // variant is a hash?
162  const QVariantHash vhash = v.toHash();
163  QHashIterator<QString, QVariant> it( vhash );
164 
165  if (indentMode == QJson::IndentMinimum) {
166  QByteArray indent = buildIndent(indentLevel);
167  str = indent + "{ ";
168  }
169  else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
170  QByteArray indent = buildIndent(indentLevel);
171  QByteArray nextindent = buildIndent(indentLevel + 1);
172  str = indent + "{\n" + nextindent;
173  }
174  else if (indentMode == QJson::IndentCompact) {
175  str = "{";
176  }
177  else {
178  str = "{ ";
179  }
180 
181  QList<QByteArray> pairs;
182  while ( it.hasNext() ) {
183  it.next();
184  indentLevel++;
185  QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
186  indentLevel--;
187  if ( !*ok ) {
188  break;
189  }
190  QByteArray key = sanitizeString( it.key() ).toUtf8();
191  QByteArray value = serializedValue;
192  if (indentMode == QJson::IndentCompact) {
193  pairs << key + ":" + value;
194  } else {
195  pairs << key + " : " + value;
196  }
197  }
198 
199  if (indentMode == QJson::IndentFull) {
200  QByteArray indent = buildIndent(indentLevel + 1);
201  str += join( pairs, ",\n" + indent);
202  }
203  else if (indentMode == QJson::IndentCompact) {
204  str += join( pairs, "," );
205  }
206  else {
207  str += join( pairs, ", " );
208  }
209 
210  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
211  QByteArray indent = buildIndent(indentLevel);
212  str += "\n" + indent + "}";
213  }
214  else if (indentMode == QJson::IndentCompact) {
215  str += "}";
216  }
217  else {
218  str += " }";
219  }
220 
221  } else if (( v.type() == QVariant::String ) || ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
222  str = sanitizeString( v.toString() ).toUtf8();
223  } else if (( v.type() == QVariant::Double) || ((QMetaType::Type)v.type() == QMetaType::Float)) { // a double or a float?
224  const double value = v.toDouble();
225 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
226  const bool special = _isnan(value) || !_finite(value);
227 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
228  const bool special = isnan(value) || isinf(value);
229 #else
230  const bool special = std::isnan(value) || std::isinf(value);
231 #endif
232  if (special) {
233  if (specialNumbersAllowed) {
234 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
235  if (_isnan(value)) {
236 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
237  if (isnan(value)) {
238 #else
239  if (std::isnan(value)) {
240 #endif
241  str += "NaN";
242  } else {
243  if (value<0) {
244  str += '-';
245  }
246  str += "Infinity";
247  }
248  } else {
249  errorMessage += QLatin1String("Attempt to write NaN or infinity, which is not supported by json\n");
250  *ok = false;
251  }
252  } else {
253  str = QByteArray::number( value , 'g', doublePrecision);
254  if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
255  str += ".0";
256  }
257  }
258  } else if ( v.type() == QVariant::Bool ) { // boolean value?
259  str = ( v.toBool() ? "true" : "false" );
260  } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
261  str = QByteArray::number( v.value<qulonglong>() );
262  } else if ( v.type() == QVariant::UInt ) { // unsigned int number?
263  str = QByteArray::number( v.value<quint32>() );
264  } else if ( v.canConvert<qlonglong>() ) { // any signed number?
265  str = QByteArray::number( v.value<qlonglong>() );
266  } else if ( v.canConvert<int>() ) { // unsigned short number?
267  str = QByteArray::number( v.value<int>() );
268  } else if ( v.canConvert<QString>() ){ // can value be converted to string?
269  // this will catch QDate, QDateTime, QUrl, ...
270  str = sanitizeString( v.toString() ).toUtf8();
271  //TODO: catch other values like QImage, QRect, ...
272  } else {
273  *ok = false;
274  errorMessage += QLatin1String("Cannot serialize ");
275  errorMessage += v.toString();
276  errorMessage += QLatin1String(" because type ");
277  errorMessage += QLatin1String(v.typeName());
278  errorMessage += QLatin1String(" is not supported by QJson\n");
279  }
280  if ( *ok )
281  {
282  return str;
283  }
284  else
285  return QByteArray();
286 }
287 
288 QByteArray Serializer::SerializerPrivate::buildIndent(int spaces)
289 {
290  QByteArray indent;
291  if (spaces < 0) {
292  spaces = 0;
293  }
294  for (int i = 0; i < spaces; i++ ) {
295  indent += " ";
296  }
297  return indent;
298 }
299 
300 QString Serializer::SerializerPrivate::sanitizeString( QString str )
301 {
302  str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );
303 
304  // escape unicode chars
305  QString result;
306  const ushort* unicode = str.utf16();
307  unsigned int i = 0;
308 
309  while ( unicode[ i ] ) {
310  if ( unicode[ i ] < 128 ) {
311  result.append( QChar( unicode[ i ] ) );
312  }
313  else {
314  QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
315  QLatin1Char('0') );
316 
317  result.append( QLatin1String ("\\u") ).append( hexCode );
318  }
319  ++i;
320  }
321  str = result;
322 
323  str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
324  str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
325  str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
326  str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
327  str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
328  str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );
329 
330  return QString( QLatin1String( "\"%1\"" ) ).arg( str );
331 }
332 
333 Serializer::Serializer()
334  : d( new SerializerPrivate )
335 {
336 }
337 
338 Serializer::~Serializer() {
339  delete d;
340 }
341 
342 void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok)
343 {
344  Q_ASSERT( io );
345  *ok = true;
346 
347  if (!io->isOpen()) {
348  if (!io->open(QIODevice::WriteOnly)) {
349  d->errorMessage = QLatin1String("Error opening device");
350  *ok = false;
351  return;
352  }
353  }
354 
355  if (!io->isWritable()) {
356  d->errorMessage = QLatin1String("Device is not readable");
357  io->close();
358  *ok = false;
359  return;
360  }
361 
362  const QByteArray str = serialize( v, ok);
363  if (*ok && (io->write(str) != str.count())) {
364  *ok = false;
365  d->errorMessage = QLatin1String("Something went wrong while writing to IO device");
366  }
367 }
368 
369 QByteArray Serializer::serialize( const QVariant &v)
370 {
371  bool ok;
372 
373  return serialize(v, &ok);
374 }
375 
376 QByteArray Serializer::serialize( const QVariant &v, bool *ok)
377 {
378  bool _ok = true;
379  d->errorMessage.clear();
380 
381  if (ok) {
382  *ok = true;
383  } else {
384  ok = &_ok;
385  }
386 
387  return d->serialize(v, ok);
388 }
389 
391  d->specialNumbersAllowed = allow;
392 }
393 
395  return d->specialNumbersAllowed;
396 }
397 
399  d->indentMode = mode;
400 }
401 
403  d->doublePrecision = precision;
404 }
405 
407  return d->indentMode;
408 }
409 
411  return d->errorMessage;
412 }
413 

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