QJson - a Qt based library for mapping JSON data to QVariant objects

QJson home page

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List

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;
45 bool specialNumbersAllowed;
46 IndentMode indentMode;
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
390 void QJson::Serializer::allowSpecialNumbers(bool allow) {
391 d->specialNumbersAllowed = allow;
392 }
393
394 bool QJson::Serializer::specialNumbersAllowed() const {
395 return d->specialNumbersAllowed;
396 }
397
398 void QJson::Serializer::setIndentMode(IndentMode mode) {
399 d->indentMode = mode;
400 }
401
402 void QJson::Serializer::setDoublePrecision(int precision) {
403 d->doublePrecision = precision;
404 }
405
406 IndentMode QJson::Serializer::indentMode() const {
407 return d->indentMode;
408 }
409
410 QString QJson::Serializer::errorMessage() const {
411 return d->errorMessage;
412 }
413
QJson::Serializer::setIndentMode
void setIndentMode(IndentMode mode=QJson::IndentNone)
Definition: serializer.cpp:398
QJson::Serializer::allowSpecialNumbers
void allowSpecialNumbers(bool allow)
Definition: serializer.cpp:390
QJson::Serializer::indentMode
IndentMode indentMode() const
Definition: serializer.cpp:406
QJson::Serializer::serialize
void serialize(const QVariant &variant, QIODevice *out, bool *ok)
Definition: serializer.cpp:342
QJson::Serializer::specialNumbersAllowed
bool specialNumbersAllowed() const
Definition: serializer.cpp:394
QJson::Serializer::setDoublePrecision
void setDoublePrecision(int precision)
Definition: serializer.cpp:402
QJson
Definition: json_parser.hh:66
QJson::Serializer::errorMessage
QString errorMessage() const
Definition: serializer.cpp:410
QJson::IndentMode
IndentMode
How the indentation should work.
Definition: serializer.h:62

| hosts this site. |  | Send comments to: QJson Developers