1d69af60c4bc71596e8ab3ec075b8a4c8c4a89ab
[buliscores] / qjson / src / 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 Library General Public
8   * License as published by the Free Software Foundation; either
9   * version 2 of the License, or (at your option) any later version.
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   * Library General Public License for more details.
15   *
16   * You should have received a copy of the GNU Library 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 using namespace QJson;
31
32 class Serializer::SerializerPrivate {
33   public:
34     SerializerPrivate() : specialNumbersAllowed(false) {}
35     bool specialNumbersAllowed;
36     QString sanitizeString( QString str );
37 };
38
39 QString Serializer::SerializerPrivate::sanitizeString( QString str )
40 {
41   str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );
42
43   // escape unicode chars
44   QString result;
45   const ushort* unicode = str.utf16();
46   unsigned int i = 0;
47
48   while ( unicode[ i ] ) {
49     if ( unicode[ i ] < 128 ) {
50       result.append( QChar( unicode[ i ] ) );
51     }
52     else {
53       QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
54                                                            QLatin1Char('0') );
55
56       result.append( QLatin1String ("\\u") ).append( hexCode );
57     }
58     ++i;
59   }
60   str = result;
61
62   str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
63   str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
64   str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
65   str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
66   str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
67   str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );
68
69   return QString( QLatin1String( "\"%1\"" ) ).arg( str );
70 }
71
72 Serializer::Serializer()
73   : d( new SerializerPrivate )
74 {
75 }
76
77 Serializer::~Serializer() {
78   delete d;
79 }
80
81 void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok )
82 {
83   Q_ASSERT( io );
84   if (!io->isOpen()) {
85     if (!io->open(QIODevice::WriteOnly)) {
86       if ( ok != 0 )
87         *ok = false;
88       qCritical ("Error opening device");
89       return;
90     }
91   }
92
93   if (!io->isWritable()) {
94     if (ok != 0)
95       *ok = false;
96     qCritical ("Device is not readable");
97     io->close();
98     return;
99   }
100
101   const QByteArray str = serialize( v );
102   if ( !str.isNull() ) {
103     QDataStream stream( io );
104     stream << str;
105   } else {
106     if ( ok )
107       *ok = false;
108   }
109 }
110
111 static QByteArray join( const QList<QByteArray>& list, const QByteArray& sep ) {
112   QByteArray res;
113   Q_FOREACH( const QByteArray& i, list ) {
114     if ( !res.isEmpty() )
115       res += sep;
116     res += i;
117   }
118   return res;
119 }
120
121 QByteArray Serializer::serialize( const QVariant &v )
122 {
123   QByteArray str;
124   bool error = false;
125
126   if ( ! v.isValid() ) { // invalid or null?
127     str = "null";
128   } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
129     const QVariantList list = v.toList();
130     QList<QByteArray> values;
131     Q_FOREACH( const QVariant& v, list )
132     {
133       QByteArray serializedValue = serialize( v );
134       if ( serializedValue.isNull() ) {
135         error = true;
136         break;
137       }
138       values << serializedValue;
139     }
140     str = "[ " + join( values, ", " ) + " ]";
141   } else if ( v.type() == QVariant::Map ) { // variant is a map?
142     const QVariantMap vmap = v.toMap();
143     QMapIterator<QString, QVariant> it( vmap );
144     str = "{ ";
145     QList<QByteArray> pairs;
146     while ( it.hasNext() ) {
147       it.next();
148       QByteArray serializedValue = serialize( it.value() );
149       if ( serializedValue.isNull() ) {
150         error = true;
151         break;
152       }
153       pairs << d->sanitizeString( it.key() ).toUtf8() + " : " + serializedValue;
154     }
155     str += join( pairs, ", " );
156     str += " }";
157   } else if (( v.type() == QVariant::String ) ||  ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
158     str = d->sanitizeString( v.toString() ).toUtf8();
159   } else if (( v.type() == QVariant::Double) || (v.type() == QMetaType::Float)) { // a double or a float?
160     const double value = v.toDouble();
161 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
162     const bool special = _isnan(value) || !_finite(value);
163 #elif defined(Q_OS_SYMBIAN)
164     const bool special = isnan(value) || isinf(value);
165 #else
166     const bool special = std::isnan(value) || std::isinf(value);
167 #endif
168     if (special) {
169       if (specialNumbersAllowed()) {
170 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
171         if (_isnan(value)) {
172 #elif defined(Q_OS_SYMBIAN)
173         if (isnan(value)) {
174 #else
175         if (std::isnan(value)) {
176 #endif
177           str += "NaN";
178         } else {
179           if (value<0) {
180             str += "-";
181           }
182           str += "Infinity";
183         }
184       } else {
185         qCritical("Attempt to write NaN or infinity, which is not supported by json");
186         error = true;
187     }
188     } else {
189       str = QByteArray::number( value );
190       if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
191         str += ".0";
192       }
193     }
194   } else if ( v.type() == QVariant::Bool ) { // boolean value?
195     str = ( v.toBool() ? "true" : "false" );
196   } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
197     str = QByteArray::number( v.value<qulonglong>() );
198   } else if ( v.canConvert<qlonglong>() ) { // any signed number?
199     str = QByteArray::number( v.value<qlonglong>() );
200   } else if ( v.canConvert<QString>() ){ // can value be converted to string?
201     // this will catch QDate, QDateTime, QUrl, ...
202     str = d->sanitizeString( v.toString() ).toUtf8();
203     //TODO: catch other values like QImage, QRect, ...
204   } else {
205     error = true;
206   }
207   if ( !error )
208     return str;
209   else
210     return QByteArray();
211 }
212
213 void QJson::Serializer::allowSpecialNumbers(bool allow) {
214   d->specialNumbersAllowed = allow;
215 }
216
217 bool QJson::Serializer::specialNumbersAllowed() const {
218   return d->specialNumbersAllowed;
219 }