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:
SerializerPrivate()38 SerializerPrivate() :
39 specialNumbersAllowed(false),
40 indentMode(QJson::IndentNone),
41 doublePrecision(6) {
42 }
43 bool specialNumbersAllowed;
44 IndentMode indentMode;
45 int doublePrecision;
46 QByteArray buildIndent(int spaces);
47 QByteArray serialize( const QVariant &v, int reserved = 0);
48 QString sanitizeString( QString str );
49 QByteArray join( const QList<QByteArray>& list, const QByteArray& sep );
50 };
51
join(const QList<QByteArray> & list,const QByteArray & sep)52 QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) {
53 QByteArray res;
54 Q_FOREACH( const QByteArray& i, list ) {
55 if ( !res.isEmpty() )
56 res += sep;
57 res += i;
58 }
59 return res;
60 }
61
serialize(const QVariant & v,int reserved)62 QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, int reserved )
63 {
64 QByteArray str;
65 bool error = false;
66 QByteArray indent;
67
68 if ( ! v.isValid() ) { // invalid or null?
69 str = "null";
70 } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
71 const QVariantList list = v.toList();
72 QList<QByteArray> values;
73 Q_FOREACH( const QVariant& var, list )
74 {
75 reserved++;
76 QByteArray serializedValue = serialize( var,reserved );
77 reserved--;
78 if ( serializedValue.isNull() ) {
79 error = true;
80 break;
81 }
82 values << serializedValue;
83 }
84
85 if (indentMode == QJson::IndentMinimum) {
86 QByteArray indent = buildIndent(reserved - 1);
87 str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
88 }
89 else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
90 QByteArray indent = buildIndent(reserved);
91 str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
92 }
93 else if (indentMode == QJson::IndentCompact) {
94 str = "[" + join( values, "," ) + "]";
95 }
96 else {
97 str = "[ " + join( values, ", " ) + " ]";
98 }
99
100 } else if ( v.type() == QVariant::Map ) { // variant is a map?
101 const QVariantMap vmap = v.toMap();
102 QMapIterator<QString, QVariant> it( vmap );
103
104 if (indentMode == QJson::IndentMinimum) {
105 QByteArray indent = buildIndent(reserved);
106 str = indent + "{ ";
107 }
108 else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
109 QByteArray indent = buildIndent(reserved);
110 QByteArray nextindent = buildIndent(reserved + 1);
111 str = indent + "{\n" + nextindent;
112 }
113 else if (indentMode == QJson::IndentCompact) {
114 str = "{";
115 }
116 else {
117 str = "{ ";
118 }
119
120 QList<QByteArray> pairs;
121 while ( it.hasNext() ) {
122 it.next();
123 reserved++;
124 QByteArray serializedValue = serialize( it.value() , reserved);
125 reserved--;
126 if ( serializedValue.isNull() ) {
127 error = true;
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(reserved + 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(reserved);
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::String ) || ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
162 str = sanitizeString( v.toString() ).toUtf8();
163 } else if (( v.type() == QVariant::Double) || ((QMetaType::Type)v.type() == QMetaType::Float)) { // a double or a float?
164 const double value = v.toDouble();
165 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
166 const bool special = _isnan(value) || !_finite(value);
167 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
168 const bool special = isnan(value) || isinf(value);
169 #else
170 const bool special = std::isnan(value) || std::isinf(value);
171 #endif
172 if (special) {
173 if (specialNumbersAllowed) {
174 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
175 if (_isnan(value)) {
176 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
177 if (isnan(value)) {
178 #else
179 if (std::isnan(value)) {
180 #endif
181 str += "NaN";
182 } else {
183 if (value<0) {
184 str += '-';
185 }
186 str += "Infinity";
187 }
188 } else {
189 qCritical("Attempt to write NaN or infinity, which is not supported by json");
190 error = true;
191 }
192 } else {
193 str = QByteArray::number( value , 'g', doublePrecision);
194 if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
195 str += ".0";
196 }
197 }
198 } else if ( v.type() == QVariant::Bool ) { // boolean value?
199 str = ( v.toBool() ? "true" : "false" );
200 } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
201 str = QByteArray::number( v.value<qulonglong>() );
202 } else if ( v.canConvert<qlonglong>() ) { // any signed number?
203 str = QByteArray::number( v.value<qlonglong>() );
204 } else if ( v.canConvert<QString>() ){ // can value be converted to string?
205 // this will catch QDate, QDateTime, QUrl, ...
206 str = sanitizeString( v.toString() ).toUtf8();
207 //TODO: catch other values like QImage, QRect, ...
208 } else {
209 error = true;
210 }
211 if ( !error )
212 {
213 return str;
214 }
215 else
216 return QByteArray();
217 }
218
219 QByteArray Serializer::SerializerPrivate::buildIndent(int spaces)
220 {
221 QByteArray indent;
222 if (spaces < 0) {
223 spaces = 0;
224 }
225 for (int i = 0; i < spaces; i++ ) {
226 indent += " ";
227 }
228 return indent;
229 }
230
231 QString Serializer::SerializerPrivate::sanitizeString( QString str )
232 {
233 str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );
234
235 // escape unicode chars
236 QString result;
237 const ushort* unicode = str.utf16();
238 unsigned int i = 0;
239
240 while ( unicode[ i ] ) {
241 if ( unicode[ i ] < 128 ) {
242 result.append( QChar( unicode[ i ] ) );
243 }
244 else {
245 QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
246 QLatin1Char('0') );
247
248 result.append( QLatin1String ("\\u") ).append( hexCode );
249 }
250 ++i;
251 }
252 str = result;
253
254 str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
255 str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
256 str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
257 str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
258 str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
259 str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );
260
261 return QString( QLatin1String( "\"%1\"" ) ).arg( str );
262 }
263
264 Serializer::Serializer()
265 : d( new SerializerPrivate )
266 {
267 }
268
269 Serializer::~Serializer() {
270 delete d;
271 }
272
273 void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok)
274 {
275 Q_ASSERT( io );
276 if (ok)
277 *ok = false;
278
279 if (!io->isOpen()) {
280 if (!io->open(QIODevice::WriteOnly)) {
281 qCritical ("Error opening device");
282 return;
283 }
284 }
285
286 if (!io->isWritable()) {
287 qCritical ("Device is not readable");
288 io->close();
289 return;
290 }
291
292 const QByteArray str = serialize( v );
293 if (io->write(str) == str.count()) {
294 if (ok)
295 *ok = true;
296 }
297 }
298
299 QByteArray Serializer::serialize( const QVariant &v)
300 {
301 return d->serialize(v);
302 }
303
304 void QJson::Serializer::allowSpecialNumbers(bool allow) {
305 d->specialNumbersAllowed = allow;
306 }
307
308 bool QJson::Serializer::specialNumbersAllowed() const {
309 return d->specialNumbersAllowed;
310 }
311
312 void QJson::Serializer::setIndentMode(IndentMode mode) {
313 d->indentMode = mode;
314 }
315
316 void QJson::Serializer::setDoublePrecision(int precision) {
317 d->doublePrecision = precision;
318 }
319
320 IndentMode QJson::Serializer::indentMode() const {
321 return d->indentMode;
322 }
323