00001 /* This file is part of qjson 00002 * 00003 * Copyright (C) 2009 Till Adam <adam@kde.org> 00004 * Copyright (C) 2009 Flavio Castelli <flavio@castelli.name> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License version 2.1, as published by the Free Software Foundation. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "serializer.h" 00022 00023 #include <QtCore/QDataStream> 00024 #include <QtCore/QStringList> 00025 #include <QtCore/QVariant> 00026 00027 // cmath does #undef for isnan and isinf macroses what can be defined in math.h 00028 #if defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS) 00029 # include <math.h> 00030 #else 00031 # include <cmath> 00032 #endif 00033 00034 #ifdef Q_OS_SOLARIS 00035 # ifndef isinf 00036 # include <ieeefp.h> 00037 # define isinf(x) (!finite((x)) && (x)==(x)) 00038 # endif 00039 #endif 00040 00041 #ifdef _MSC_VER // using MSVC compiler 00042 #include <float.h> 00043 #endif 00044 00045 using namespace QJson; 00046 00047 class Serializer::SerializerPrivate { 00048 public: 00049 SerializerPrivate() : 00050 specialNumbersAllowed(false), 00051 indentMode(QJson::IndentNone), 00052 doublePrecision(6) { 00053 errorMessage.clear(); 00054 } 00055 QString errorMessage; 00056 bool specialNumbersAllowed; 00057 IndentMode indentMode; 00058 int doublePrecision; 00059 00060 QByteArray serialize( const QVariant &v, bool *ok, int indentLevel = 0); 00061 00062 static QByteArray buildIndent(int spaces); 00063 static QByteArray escapeString( const QString& str ); 00064 static QByteArray join( const QList<QByteArray>& list, const QByteArray& sep ); 00065 }; 00066 00067 QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) { 00068 QByteArray res; 00069 Q_FOREACH( const QByteArray& i, list ) { 00070 if ( !res.isEmpty() ) 00071 res += sep; 00072 res += i; 00073 } 00074 return res; 00075 } 00076 00077 QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, bool *ok, int indentLevel) 00078 { 00079 QByteArray str; 00080 00081 if ( ! v.isValid() ) { // invalid or null? 00082 str = "null"; 00083 } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist? 00084 const QVariantList list = v.toList(); 00085 QList<QByteArray> values; 00086 Q_FOREACH( const QVariant& var, list ) 00087 { 00088 QByteArray serializedValue; 00089 00090 serializedValue = serialize( var, ok, indentLevel+1); 00091 00092 if ( !*ok ) { 00093 break; 00094 } 00095 switch(indentMode) { 00096 case QJson::IndentFull : 00097 case QJson::IndentMedium : 00098 case QJson::IndentMinimum : 00099 values << serializedValue; 00100 break; 00101 case QJson::IndentCompact : 00102 case QJson::IndentNone : 00103 default: 00104 values << serializedValue.trimmed(); 00105 break; 00106 } 00107 } 00108 00109 if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull ) { 00110 QByteArray indent = buildIndent(indentLevel); 00111 str = indent + "[\n" + join( values, ",\n" ) + "\n" + indent + "]"; 00112 } 00113 else if (indentMode == QJson::IndentMinimum) { 00114 QByteArray indent = buildIndent(indentLevel); 00115 str = indent + "[\n" + join( values, ",\n" ) + "\n" + indent + "]"; 00116 } 00117 else if (indentMode == QJson::IndentCompact) { 00118 str = "[" + join( values, "," ) + "]"; 00119 } 00120 else { 00121 str = "[ " + join( values, ", " ) + " ]"; 00122 } 00123 00124 } else if ( v.type() == QVariant::Map ) { // variant is a map? 00125 const QVariantMap vmap = v.toMap(); 00126 QMapIterator<QString, QVariant> it( vmap ); 00127 00128 if (indentMode == QJson::IndentMinimum) { 00129 QByteArray indent = buildIndent(indentLevel); 00130 str = indent + "{ "; 00131 } 00132 else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) { 00133 QByteArray indent = buildIndent(indentLevel); 00134 QByteArray nextindent = buildIndent(indentLevel + 1); 00135 str = indent + "{\n" + nextindent; 00136 } 00137 else if (indentMode == QJson::IndentCompact) { 00138 str = "{"; 00139 } 00140 else { 00141 str = "{ "; 00142 } 00143 00144 QList<QByteArray> pairs; 00145 while ( it.hasNext() ) { 00146 it.next(); 00147 indentLevel++; 00148 QByteArray serializedValue = serialize( it.value(), ok, indentLevel); 00149 indentLevel--; 00150 if ( !*ok ) { 00151 break; 00152 } 00153 QByteArray key = escapeString( it.key() ); 00154 QByteArray value = serializedValue.trimmed(); 00155 if (indentMode == QJson::IndentCompact) { 00156 pairs << key + ":" + value; 00157 } else { 00158 pairs << key + " : " + value; 00159 } 00160 } 00161 00162 if (indentMode == QJson::IndentFull) { 00163 QByteArray indent = buildIndent(indentLevel + 1); 00164 str += join( pairs, ",\n" + indent); 00165 } 00166 else if (indentMode == QJson::IndentCompact) { 00167 str += join( pairs, "," ); 00168 } 00169 else { 00170 str += join( pairs, ", " ); 00171 } 00172 00173 if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) { 00174 QByteArray indent = buildIndent(indentLevel); 00175 str += "\n" + indent + "}"; 00176 } 00177 else if (indentMode == QJson::IndentCompact) { 00178 str += "}"; 00179 } 00180 else { 00181 str += " }"; 00182 } 00183 00184 } else if ( v.type() == QVariant::Hash ) { // variant is a hash? 00185 const QVariantHash vhash = v.toHash(); 00186 QHashIterator<QString, QVariant> it( vhash ); 00187 00188 if (indentMode == QJson::IndentMinimum) { 00189 QByteArray indent = buildIndent(indentLevel); 00190 str = indent + "{ "; 00191 } 00192 else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) { 00193 QByteArray indent = buildIndent(indentLevel); 00194 QByteArray nextindent = buildIndent(indentLevel + 1); 00195 str = indent + "{\n" + nextindent; 00196 } 00197 else if (indentMode == QJson::IndentCompact) { 00198 str = "{"; 00199 } 00200 else { 00201 str = "{ "; 00202 } 00203 00204 QList<QByteArray> pairs; 00205 while ( it.hasNext() ) { 00206 it.next(); 00207 00208 QByteArray serializedValue = serialize( it.value(), ok, indentLevel + 1); 00209 00210 if ( !*ok ) { 00211 break; 00212 } 00213 QByteArray key = escapeString( it.key() ); 00214 QByteArray value = serializedValue.trimmed(); 00215 if (indentMode == QJson::IndentCompact) { 00216 pairs << key + ":" + value; 00217 } else { 00218 pairs << key + " : " + value; 00219 } 00220 } 00221 00222 if (indentMode == QJson::IndentFull) { 00223 QByteArray indent = buildIndent(indentLevel + 1); 00224 str += join( pairs, ",\n" + indent); 00225 } 00226 else if (indentMode == QJson::IndentCompact) { 00227 str += join( pairs, "," ); 00228 } 00229 else { 00230 str += join( pairs, ", " ); 00231 } 00232 00233 if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) { 00234 QByteArray indent = buildIndent(indentLevel); 00235 str += "\n" + indent + "}"; 00236 } 00237 else if (indentMode == QJson::IndentCompact) { 00238 str += "}"; 00239 } 00240 else { 00241 str += " }"; 00242 } 00243 00244 } else { 00245 // Add indent, we may need to remove it later for some layouts 00246 switch(indentMode) { 00247 case QJson::IndentFull : 00248 case QJson::IndentMedium : 00249 case QJson::IndentMinimum : 00250 str += buildIndent(indentLevel); 00251 break; 00252 case QJson::IndentCompact : 00253 case QJson::IndentNone : 00254 default: 00255 break; 00256 } 00257 00258 if (( v.type() == QVariant::String ) || ( v.type() == QVariant::ByteArray )) { // a string or a byte array? 00259 str += escapeString( v.toString() ); 00260 } else if (( v.type() == QVariant::Double) || ((QMetaType::Type)v.type() == QMetaType::Float)) { // a double or a float? 00261 const double value = v.toDouble(); 00262 #if defined _WIN32 && !defined(Q_OS_SYMBIAN) 00263 const bool special = _isnan(value) || !_finite(value); 00264 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS) 00265 const bool special = isnan(value) || isinf(value); 00266 #else 00267 const bool special = std::isnan(value) || std::isinf(value); 00268 #endif 00269 if (special) { 00270 if (specialNumbersAllowed) { 00271 #if defined _WIN32 && !defined(Q_OS_SYMBIAN) 00272 if (_isnan(value)) { 00273 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS) 00274 if (isnan(value)) { 00275 #else 00276 if (std::isnan(value)) { 00277 #endif 00278 str += "NaN"; 00279 } else { 00280 if (value<0) { 00281 str += '-'; 00282 } 00283 str += "Infinity"; 00284 } 00285 } else { 00286 errorMessage += QLatin1String("Attempt to write NaN or infinity, which is not supported by json\n"); 00287 *ok = false; 00288 } 00289 } else { 00290 str = QByteArray::number( value , 'g', doublePrecision); 00291 if( ! str.contains( "." ) && ! str.contains( "e" ) ) { 00292 str += ".0"; 00293 } 00294 } 00295 } else if ( v.type() == QVariant::Bool ) { // boolean value? 00296 str += ( v.toBool() ? "true" : "false" ); 00297 } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number? 00298 str += QByteArray::number( v.value<qulonglong>() ); 00299 } else if ( v.type() == QVariant::UInt ) { // unsigned int number? 00300 str += QByteArray::number( v.value<quint32>() ); 00301 } else if ( v.canConvert<qlonglong>() ) { // any signed number? 00302 str += QByteArray::number( v.value<qlonglong>() ); 00303 } else if ( v.canConvert<int>() ) { // unsigned short number? 00304 str += QByteArray::number( v.value<int>() ); 00305 } else if ( v.canConvert<QString>() ){ // can value be converted to string? 00306 // this will catch QDate, QDateTime, QUrl, ... 00307 str += escapeString( v.toString() ); 00308 //TODO: catch other values like QImage, QRect, ... 00309 } else { 00310 *ok = false; 00311 errorMessage += QLatin1String("Cannot serialize "); 00312 errorMessage += v.toString(); 00313 errorMessage += QLatin1String(" because type "); 00314 errorMessage += QLatin1String(v.typeName()); 00315 errorMessage += QLatin1String(" is not supported by QJson\n"); 00316 } 00317 } 00318 if ( *ok ) 00319 { 00320 return str; 00321 } 00322 else 00323 return QByteArray(); 00324 } 00325 00326 QByteArray Serializer::SerializerPrivate::buildIndent(int spaces) 00327 { 00328 QByteArray indent; 00329 if (spaces < 0) { 00330 spaces = 0; 00331 } 00332 for (int i = 0; i < spaces; i++ ) { 00333 indent += " "; 00334 } 00335 return indent; 00336 } 00337 00338 QByteArray Serializer::SerializerPrivate::escapeString( const QString& str ) 00339 { 00340 QByteArray result; 00341 result.reserve(str.size() + 2); 00342 result.append('\"'); 00343 for (QString::const_iterator it = str.begin(); it != str.end(); it++) { 00344 ushort unicode = it->unicode(); 00345 switch ( unicode ) { 00346 case '\"': 00347 result.append("\\\""); 00348 break; 00349 case '\\': 00350 result.append("\\\\"); 00351 break; 00352 case '\b': 00353 result.append("\\b"); 00354 break; 00355 case '\f': 00356 result.append("\\f"); 00357 break; 00358 case '\n': 00359 result.append("\\n"); 00360 break; 00361 case '\r': 00362 result.append("\\r"); 00363 break; 00364 case '\t': 00365 result.append("\\t"); 00366 break; 00367 default: 00368 if ( unicode > 0x1F && unicode < 128 ) { 00369 result.append(static_cast<char>(unicode)); 00370 } else { 00371 char escaped[7]; 00372 qsnprintf(escaped, sizeof(escaped)/sizeof(char), "\\u%04x", unicode); 00373 result.append(escaped); 00374 } 00375 } 00376 } 00377 result.append('\"'); 00378 return result; 00379 } 00380 00381 Serializer::Serializer() 00382 : d( new SerializerPrivate ) 00383 { 00384 } 00385 00386 Serializer::~Serializer() { 00387 delete d; 00388 } 00389 00390 void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok) 00391 { 00392 Q_ASSERT( io ); 00393 *ok = true; 00394 00395 if (!io->isOpen()) { 00396 if (!io->open(QIODevice::WriteOnly)) { 00397 d->errorMessage = QLatin1String("Error opening device"); 00398 *ok = false; 00399 return; 00400 } 00401 } 00402 00403 if (!io->isWritable()) { 00404 d->errorMessage = QLatin1String("Device is not readable"); 00405 io->close(); 00406 *ok = false; 00407 return; 00408 } 00409 00410 const QByteArray str = serialize( v, ok); 00411 if (*ok && (io->write(str) != str.count())) { 00412 *ok = false; 00413 d->errorMessage = QLatin1String("Something went wrong while writing to IO device"); 00414 } 00415 } 00416 00417 QByteArray Serializer::serialize( const QVariant &v) 00418 { 00419 bool ok; 00420 00421 return serialize(v, &ok); 00422 } 00423 00424 QByteArray Serializer::serialize( const QVariant &v, bool *ok) 00425 { 00426 bool _ok = true; 00427 d->errorMessage.clear(); 00428 00429 if (ok) { 00430 *ok = true; 00431 } else { 00432 ok = &_ok; 00433 } 00434 00435 return d->serialize(v, ok); 00436 } 00437 00438 void QJson::Serializer::allowSpecialNumbers(bool allow) { 00439 d->specialNumbersAllowed = allow; 00440 } 00441 00442 bool QJson::Serializer::specialNumbersAllowed() const { 00443 return d->specialNumbersAllowed; 00444 } 00445 00446 void QJson::Serializer::setIndentMode(IndentMode mode) { 00447 d->indentMode = mode; 00448 } 00449 00450 void QJson::Serializer::setDoublePrecision(int precision) { 00451 d->doublePrecision = precision; 00452 } 00453 00454 IndentMode QJson::Serializer::indentMode() const { 00455 return d->indentMode; 00456 } 00457 00458 QString QJson::Serializer::errorMessage() const { 00459 return d->errorMessage; 00460 } 00461
|
hosts this site. |
Send comments to: QJson Developers |