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

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