1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <qjsondocument.h>
41#include <qjsonobject.h>
42#include <qjsonvalue.h>
43#include <qjsonarray.h>
44#include <qstringlist.h>
45#include <qvariant.h>
46#include <qdebug.h>
47#include <qcbormap.h>
48#include <qcborarray.h>
49#include "qcborvalue_p.h"
50#include "qjsonwriter_p.h"
51#include "qjsonparser_p.h"
52#include "qjson_p.h"
53#include "qdatastream.h"
54
55#include <private/qmemory_p.h>
56
57QT_BEGIN_NAMESPACE
58
59/*! \class QJsonDocument
60 \inmodule QtCore
61 \ingroup json
62 \ingroup shared
63 \reentrant
64 \since 5.0
65
66 \brief The QJsonDocument class provides a way to read and write JSON documents.
67
68 QJsonDocument is a class that wraps a complete JSON document and can read
69 this document from, and write it to, a UTF-8 encoded text-based
70 representation.
71
72 A JSON document can be converted from its text-based representation to a QJsonDocument
73 using QJsonDocument::fromJson(). toJson() converts it back to text. The parser is very
74 fast and efficient and converts the JSON to the binary representation used by Qt.
75
76 Validity of the parsed document can be queried with !isNull()
77
78 A document can be queried as to whether it contains an array or an object using isArray()
79 and isObject(). The array or object contained in the document can be retrieved using
80 array() or object() and then read or manipulated.
81
82 \sa {JSON Support in Qt}, {JSON Save Game Example}
83*/
84
85
86class QJsonDocumentPrivate
87{
88 Q_DISABLE_COPY_MOVE(QJsonDocumentPrivate);
89public:
90 QJsonDocumentPrivate() = default;
91 QJsonDocumentPrivate(QCborValue data) : value(std::move(data)) {}
92 ~QJsonDocumentPrivate()
93 {
94 if (rawData)
95 free(rawData);
96 }
97
98 QCborValue value;
99 char *rawData = nullptr;
100 uint rawDataSize = 0;
101
102 void clearRawData()
103 {
104 if (rawData) {
105 free(rawData);
106 rawData = nullptr;
107 rawDataSize = 0;
108 }
109 }
110};
111
112/*!
113 * Constructs an empty and invalid document.
114 */
115QJsonDocument::QJsonDocument()
116 : d(nullptr)
117{
118}
119
120/*!
121 * Creates a QJsonDocument from \a object.
122 */
123QJsonDocument::QJsonDocument(const QJsonObject &object)
124 : d(nullptr)
125{
126 setObject(object);
127}
128
129/*!
130 * Constructs a QJsonDocument from \a array.
131 */
132QJsonDocument::QJsonDocument(const QJsonArray &array)
133 : d(nullptr)
134{
135 setArray(array);
136}
137
138/*!
139 \internal
140 */
141QJsonDocument::QJsonDocument(const QCborValue &data)
142 : d(qt_make_unique<QJsonDocumentPrivate>(data))
143{
144 Q_ASSERT(d);
145}
146
147/*!
148 Deletes the document.
149
150 Binary data set with fromRawData is not freed.
151 */
152QJsonDocument::~QJsonDocument() = default;
153
154/*!
155 * Creates a copy of the \a other document.
156 */
157QJsonDocument::QJsonDocument(const QJsonDocument &other)
158{
159 if (other.d) {
160 if (!d)
161 d = qt_make_unique<QJsonDocumentPrivate>();
162 d->value = other.d->value;
163 } else {
164 d.reset();
165 }
166}
167
168QJsonDocument::QJsonDocument(QJsonDocument &&other) noexcept
169 : d(std::move(other.d))
170{
171}
172
173void QJsonDocument::swap(QJsonDocument &other) noexcept
174{
175 qSwap(d, other.d);
176}
177
178/*!
179 * Assigns the \a other document to this QJsonDocument.
180 * Returns a reference to this object.
181 */
182QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
183{
184 if (this != &other) {
185 if (other.d) {
186 if (!d)
187 d = qt_make_unique<QJsonDocumentPrivate>();
188 else
189 d->clearRawData();
190 d->value = other.d->value;
191 } else {
192 d.reset();
193 }
194 }
195 return *this;
196}
197
198/*!
199 \fn QJsonDocument::QJsonDocument(QJsonDocument &&other)
200 \since 5.10
201
202 Move-constructs a QJsonDocument from \a other.
203*/
204
205/*!
206 \fn QJsonDocument &QJsonDocument::operator =(QJsonDocument &&other)
207 \since 5.10
208
209 Move-assigns \a other to this document.
210*/
211
212/*!
213 \fn void QJsonDocument::swap(QJsonDocument &other)
214 \since 5.10
215
216 Swaps the document \a other with this. This operation is very fast and never fails.
217*/
218
219/*!
220 Creates a QJsonDocument from the QVariant \a variant.
221
222 If the \a variant contains any other type than a QVariantMap,
223 QVariantHash, QVariantList or QStringList, the returned document is invalid.
224
225 \sa toVariant()
226 */
227QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
228{
229 QJsonDocument doc;
230
231 switch (variant.metaType().id()) {
232 case QMetaType::QVariantMap:
233 doc.setObject(QJsonObject::fromVariantMap(variant.toMap()));
234 break;
235 case QMetaType::QVariantHash:
236 doc.setObject(QJsonObject::fromVariantHash(variant.toHash()));
237 break;
238 case QMetaType::QVariantList:
239 doc.setArray(QJsonArray::fromVariantList(variant.toList()));
240 break;
241 case QMetaType::QStringList:
242 doc.d = qt_make_unique<QJsonDocumentPrivate>();
243 doc.d->value = QCborArray::fromStringList(variant.toStringList());
244 break;
245 default:
246 break;
247 }
248 return doc;
249}
250
251/*!
252 Returns a QVariant representing the Json document.
253
254 The returned variant will be a QVariantList if the document is
255 a QJsonArray and a QVariantMap if the document is a QJsonObject.
256
257 \sa fromVariant(), QJsonValue::toVariant()
258 */
259QVariant QJsonDocument::toVariant() const
260{
261 if (!d)
262 return QVariant();
263
264 QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
265 if (d->value.isArray())
266 return QJsonArray(container).toVariantList();
267 return QJsonObject(container).toVariantMap();
268}
269
270/*!
271 \enum QJsonDocument::JsonFormat
272 \since 5.1
273
274 This value defines the format of the JSON byte array produced
275 when converting to a QJsonDocument using toJson().
276
277 \value Indented Defines human readable output as follows:
278 \snippet code/src_corelib_serialization_qjsondocument.cpp 0
279
280 \value Compact Defines a compact output as follows:
281 \snippet code/src_corelib_serialization_qjsondocument.cpp 1
282 */
283
284/*!
285 \since 5.1
286 Converts the QJsonDocument to a UTF-8 encoded JSON document in the provided \a format.
287
288 \sa fromJson(), JsonFormat
289 */
290#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC)
291QByteArray QJsonDocument::toJson(JsonFormat format) const
292{
293 QByteArray json;
294 if (!d)
295 return json;
296
297 const QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
298 if (d->value.isArray())
299 QJsonPrivate::Writer::arrayToJson(container, json, 0, (format == Compact));
300 else
301 QJsonPrivate::Writer::objectToJson(container, json, 0, (format == Compact));
302
303 return json;
304}
305#endif
306
307/*!
308 Parses \a json as a UTF-8 encoded JSON document, and creates a QJsonDocument
309 from it.
310
311 Returns a valid (non-null) QJsonDocument if the parsing succeeds. If it fails,
312 the returned document will be null, and the optional \a error variable will contain
313 further details about the error.
314
315 \sa toJson(), QJsonParseError, isNull()
316 */
317QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error)
318{
319 QJsonPrivate::Parser parser(json.constData(), json.length());
320 QJsonDocument result;
321 const QCborValue val = parser.parse(error);
322 if (val.isArray() || val.isMap()) {
323 result.d = qt_make_unique<QJsonDocumentPrivate>();
324 result.d->value = val;
325 }
326 return result;
327}
328
329/*!
330 Returns \c true if the document doesn't contain any data.
331 */
332bool QJsonDocument::isEmpty() const
333{
334 if (!d)
335 return true;
336
337 return false;
338}
339
340/*!
341 Returns \c true if the document contains an array.
342
343 \sa array(), isObject()
344 */
345bool QJsonDocument::isArray() const
346{
347 if (!d)
348 return false;
349
350 return d->value.isArray();
351}
352
353/*!
354 Returns \c true if the document contains an object.
355
356 \sa object(), isArray()
357 */
358bool QJsonDocument::isObject() const
359{
360 if (!d)
361 return false;
362
363 return d->value.isMap();
364}
365
366/*!
367 Returns the QJsonObject contained in the document.
368
369 Returns an empty object if the document contains an
370 array.
371
372 \sa isObject(), array(), setObject()
373 */
374QJsonObject QJsonDocument::object() const
375{
376 if (isObject()) {
377 if (auto container = QJsonPrivate::Value::container(d->value))
378 return QJsonObject(container);
379 }
380 return QJsonObject();
381}
382
383/*!
384 Returns the QJsonArray contained in the document.
385
386 Returns an empty array if the document contains an
387 object.
388
389 \sa isArray(), object(), setArray()
390 */
391QJsonArray QJsonDocument::array() const
392{
393 if (isArray()) {
394 if (auto container = QJsonPrivate::Value::container(d->value))
395 return QJsonArray(container);
396 }
397 return QJsonArray();
398}
399
400/*!
401 Sets \a object as the main object of this document.
402
403 \sa setArray(), object()
404 */
405void QJsonDocument::setObject(const QJsonObject &object)
406{
407 if (!d)
408 d = qt_make_unique<QJsonDocumentPrivate>();
409 else
410 d->clearRawData();
411
412 d->value = QCborValue::fromJsonValue(object);
413}
414
415/*!
416 Sets \a array as the main object of this document.
417
418 \sa setObject(), array()
419 */
420void QJsonDocument::setArray(const QJsonArray &array)
421{
422 if (!d)
423 d = qt_make_unique<QJsonDocumentPrivate>();
424 else
425 d->clearRawData();
426
427 d->value = QCborValue::fromJsonValue(array);
428}
429
430#if QT_STRINGVIEW_LEVEL < 2
431/*!
432 Returns a QJsonValue representing the value for the key \a key.
433
434 Equivalent to calling object().value(key).
435
436 The returned QJsonValue is QJsonValue::Undefined if the key does not exist,
437 or if isObject() is false.
438
439 \since 5.10
440
441 \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject
442 */
443const QJsonValue QJsonDocument::operator[](const QString &key) const
444{
445 return (*this)[QStringView(key)];
446}
447#endif
448
449/*!
450 \overload
451 \since 5.14
452*/
453const QJsonValue QJsonDocument::operator[](QStringView key) const
454{
455 if (!isObject())
456 return QJsonValue(QJsonValue::Undefined);
457
458 return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key));
459}
460
461/*!
462 \overload
463 \since 5.10
464*/
465const QJsonValue QJsonDocument::operator[](QLatin1String key) const
466{
467 if (!isObject())
468 return QJsonValue(QJsonValue::Undefined);
469
470 return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key));
471}
472
473/*!
474 Returns a QJsonValue representing the value for index \a i.
475
476 Equivalent to calling array().at(i).
477
478 The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds,
479 or if isArray() is false.
480
481 \since 5.10
482
483 \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray
484 */
485const QJsonValue QJsonDocument::operator[](qsizetype i) const
486{
487 if (!isArray())
488 return QJsonValue(QJsonValue::Undefined);
489
490 return QJsonPrivate::Value::fromTrustedCbor(d->value.toArray().at(i));
491}
492
493/*!
494 Returns \c true if the \a other document is equal to this document.
495 */
496bool QJsonDocument::operator==(const QJsonDocument &other) const
497{
498 if (d && other.d)
499 return d->value == other.d->value;
500 return !d == !other.d;
501}
502
503/*!
504 \fn bool QJsonDocument::operator!=(const QJsonDocument &other) const
505
506 returns \c true if \a other is not equal to this document
507 */
508
509/*!
510 returns \c true if this document is null.
511
512 Null documents are documents created through the default constructor.
513
514 Documents created from UTF-8 encoded text or the binary format are
515 validated during parsing. If validation fails, the returned document
516 will also be null.
517 */
518bool QJsonDocument::isNull() const
519{
520 return (d == nullptr);
521}
522
523#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
524QDebug operator<<(QDebug dbg, const QJsonDocument &o)
525{
526 QDebugStateSaver saver(dbg);
527 if (!o.d) {
528 dbg << "QJsonDocument()";
529 return dbg;
530 }
531 QByteArray json;
532 const QCborContainerPrivate *container = QJsonPrivate::Value::container(o.d->value);
533 if (o.d->value.isArray())
534 QJsonPrivate::Writer::arrayToJson(container, json, 0, true);
535 else
536 QJsonPrivate::Writer::objectToJson(container, json, 0, true);
537 dbg.nospace() << "QJsonDocument("
538 << json.constData() // print as utf-8 string without extra quotation marks
539 << ')';
540 return dbg;
541}
542#endif
543
544#ifndef QT_NO_DATASTREAM
545QDataStream &operator<<(QDataStream &stream, const QJsonDocument &doc)
546{
547 stream << doc.toJson(QJsonDocument::Compact);
548 return stream;
549}
550
551QDataStream &operator>>(QDataStream &stream, QJsonDocument &doc)
552{
553 QByteArray buffer;
554 stream >> buffer;
555 QJsonParseError parseError{};
556 doc = QJsonDocument::fromJson(buffer, &parseError);
557 if (parseError.error && !buffer.isEmpty())
558 stream.setStatus(QDataStream::ReadCorruptData);
559 return stream;
560}
561#endif
562
563QT_END_NAMESPACE
564