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 "qjsonwriter_p.h"
48#include "qjsonparser_p.h"
49#include "qjson_p.h"
50#include "qdatastream.h"
51
52QT_BEGIN_NAMESPACE
53
54/*! \class QJsonDocument
55 \inmodule QtCore
56 \ingroup json
57 \ingroup shared
58 \reentrant
59 \since 5.0
60
61 \brief The QJsonDocument class provides a way to read and write JSON documents.
62
63 QJsonDocument is a class that wraps a complete JSON document and can read and
64 write this document both from a UTF-8 encoded text based representation as well
65 as Qt's own binary format.
66
67 A JSON document can be converted from its text-based representation to a QJsonDocument
68 using QJsonDocument::fromJson(). toJson() converts it back to text. The parser is very
69 fast and efficient and converts the JSON to the binary representation used by Qt.
70
71 Validity of the parsed document can be queried with !isNull()
72
73 A document can be queried as to whether it contains an array or an object using isArray()
74 and isObject(). The array or object contained in the document can be retrieved using
75 array() or object() and then read or manipulated.
76
77 A document can also be created from a stored binary representation using fromBinaryData() or
78 fromRawData().
79
80 \sa {JSON Support in Qt}, {JSON Save Game Example}
81*/
82
83/*!
84 * Constructs an empty and invalid document.
85 */
86QJsonDocument::QJsonDocument()
87 : d(nullptr)
88{
89}
90
91/*!
92 * Creates a QJsonDocument from \a object.
93 */
94QJsonDocument::QJsonDocument(const QJsonObject &object)
95 : d(nullptr)
96{
97 setObject(object);
98}
99
100/*!
101 * Constructs a QJsonDocument from \a array.
102 */
103QJsonDocument::QJsonDocument(const QJsonArray &array)
104 : d(nullptr)
105{
106 setArray(array);
107}
108
109/*!
110 \internal
111 */
112QJsonDocument::QJsonDocument(QJsonPrivate::Data *data)
113 : d(data)
114{
115 Q_ASSERT(d);
116 d->ref.ref();
117}
118
119/*!
120 Deletes the document.
121
122 Binary data set with fromRawData is not freed.
123 */
124QJsonDocument::~QJsonDocument()
125{
126 if (d && !d->ref.deref())
127 delete d;
128}
129
130/*!
131 * Creates a copy of the \a other document.
132 */
133QJsonDocument::QJsonDocument(const QJsonDocument &other)
134{
135 d = other.d;
136 if (d)
137 d->ref.ref();
138}
139
140/*!
141 * Assigns the \a other document to this QJsonDocument.
142 * Returns a reference to this object.
143 */
144QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
145{
146 if (d != other.d) {
147 if (d && !d->ref.deref())
148 delete d;
149 d = other.d;
150 if (d)
151 d->ref.ref();
152 }
153
154 return *this;
155}
156
157/*!
158 \fn QJsonDocument::QJsonDocument(QJsonDocument &&other)
159 \since 5.10
160
161 Move-constructs a QJsonDocument from \a other.
162*/
163
164/*!
165 \fn QJsonDocument &QJsonDocument::operator =(QJsonDocument &&other)
166 \since 5.10
167
168 Move-assigns \a other to this document.
169*/
170
171/*!
172 \fn void QJsonDocument::swap(QJsonDocument &other)
173 \since 5.10
174
175 Swaps the document \a other with this. This operation is very fast and never fails.
176*/
177
178
179/*! \enum QJsonDocument::DataValidation
180
181 This value is used to tell QJsonDocument whether to validate the binary data
182 when converting to a QJsonDocument using fromBinaryData() or fromRawData().
183
184 \value Validate Validate the data before using it. This is the default.
185 \value BypassValidation Bypasses data validation. Only use if you received the
186 data from a trusted place and know it's valid, as using of invalid data can crash
187 the application.
188 */
189
190/*!
191 Creates a QJsonDocument that uses the first \a size bytes from
192 \a data. It assumes \a data contains a binary encoded JSON document.
193 The created document does not take ownership of \a data and the caller
194 has to guarantee that \a data will not be deleted or modified as long as
195 any QJsonDocument, QJsonObject or QJsonArray still references the data.
196
197 \a data has to be aligned to a 4 byte boundary.
198
199 \a validation decides whether the data is checked for validity before being used.
200 By default the data is validated. If the \a data is not valid, the method returns
201 a null document.
202
203 Returns a QJsonDocument representing the data.
204
205 \sa rawData(), fromBinaryData(), isNull(), DataValidation
206 */
207QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation)
208{
209 if (quintptr(data) & 3) {
210 qWarning("QJsonDocument::fromRawData: data has to have 4 byte alignment");
211 return QJsonDocument();
212 }
213
214 if (size < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)))
215 return QJsonDocument();
216
217 QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size);
218 d->ownsData = false;
219
220 if (validation != BypassValidation && !d->valid()) {
221 delete d;
222 return QJsonDocument();
223 }
224
225 return QJsonDocument(d);
226}
227
228/*!
229 Returns the raw binary representation of the data
230 \a size will contain the size of the returned data.
231
232 This method is useful to e.g. stream the JSON document
233 in it's binary form to a file.
234 */
235const char *QJsonDocument::rawData(int *size) const
236{
237 if (!d) {
238 *size = 0;
239 return nullptr;
240 }
241 *size = d->alloc;
242 return d->rawData;
243}
244
245/*!
246 Creates a QJsonDocument from \a data.
247
248 \a validation decides whether the data is checked for validity before being used.
249 By default the data is validated. If the \a data is not valid, the method returns
250 a null document.
251
252 \sa toBinaryData(), fromRawData(), isNull(), DataValidation
253 */
254QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation)
255{
256 if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)))
257 return QJsonDocument();
258
259 QJsonPrivate::Header h;
260 memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header));
261 QJsonPrivate::Base root;
262 memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base));
263
264 // do basic checks here, so we don't try to allocate more memory than we can.
265 if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u ||
266 sizeof(QJsonPrivate::Header) + root.size > (uint)data.size())
267 return QJsonDocument();
268
269 const uint size = sizeof(QJsonPrivate::Header) + root.size;
270 char *raw = (char *)malloc(size);
271 if (!raw)
272 return QJsonDocument();
273
274 memcpy(raw, data.constData(), size);
275 QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size);
276
277 if (validation != BypassValidation && !d->valid()) {
278 delete d;
279 return QJsonDocument();
280 }
281
282 return QJsonDocument(d);
283}
284
285/*!
286 Creates a QJsonDocument from the QVariant \a variant.
287
288 If the \a variant contains any other type than a QVariantMap,
289 QVariantHash, QVariantList or QStringList, the returned document is invalid.
290
291 \sa toVariant()
292 */
293QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
294{
295 QJsonDocument doc;
296 switch (variant.type()) {
297 case QVariant::Map:
298 doc.setObject(QJsonObject::fromVariantMap(variant.toMap()));
299 break;
300 case QVariant::Hash:
301 doc.setObject(QJsonObject::fromVariantHash(variant.toHash()));
302 break;
303 case QVariant::List:
304 doc.setArray(QJsonArray::fromVariantList(variant.toList()));
305 break;
306 case QVariant::StringList:
307 doc.setArray(QJsonArray::fromStringList(variant.toStringList()));
308 break;
309 default:
310 break;
311 }
312 return doc;
313}
314
315/*!
316 Returns a QVariant representing the Json document.
317
318 The returned variant will be a QVariantList if the document is
319 a QJsonArray and a QVariantMap if the document is a QJsonObject.
320
321 \sa fromVariant(), QJsonValue::toVariant()
322 */
323QVariant QJsonDocument::toVariant() const
324{
325 if (!d)
326 return QVariant();
327
328 if (d->header->root()->isArray())
329 return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root())).toVariantList();
330 else
331 return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root())).toVariantMap();
332}
333
334/*!
335 Converts the QJsonDocument to a UTF-8 encoded JSON document.
336
337 \sa fromJson()
338 */
339#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC)
340QByteArray QJsonDocument::toJson() const
341{
342 return toJson(Indented);
343}
344#endif
345
346/*!
347 \enum QJsonDocument::JsonFormat
348 \since 5.1
349
350 This value defines the format of the JSON byte array produced
351 when converting to a QJsonDocument using toJson().
352
353 \value Indented Defines human readable output as follows:
354 \snippet code/src_corelib_serialization_qjsondocument.cpp 0
355
356 \value Compact Defines a compact output as follows:
357 \snippet code/src_corelib_serialization_qjsondocument.cpp 1
358 */
359
360/*!
361 \since 5.1
362 Converts the QJsonDocument to a UTF-8 encoded JSON document in the provided \a format.
363
364 \sa fromJson(), JsonFormat
365 */
366#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC)
367QByteArray QJsonDocument::toJson(JsonFormat format) const
368{
369 QByteArray json;
370 if (!d)
371 return json;
372
373 if (d->header->root()->isArray())
374 QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(d->header->root()), json, 0, (format == Compact));
375 else
376 QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(d->header->root()), json, 0, (format == Compact));
377
378 return json;
379}
380#endif
381
382/*!
383 Parses \a json as a UTF-8 encoded JSON document, and creates a QJsonDocument
384 from it.
385
386 Returns a valid (non-null) QJsonDocument if the parsing succeeds. If it fails,
387 the returned document will be null, and the optional \a error variable will contain
388 further details about the error.
389
390 \sa toJson(), QJsonParseError, isNull()
391 */
392QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error)
393{
394 QJsonPrivate::Parser parser(json.constData(), json.length());
395 return parser.parse(error);
396}
397
398/*!
399 Returns \c true if the document doesn't contain any data.
400 */
401bool QJsonDocument::isEmpty() const
402{
403 if (!d)
404 return true;
405
406 return false;
407}
408
409/*!
410 Returns a binary representation of the document.
411
412 The binary representation is also the native format used internally in Qt,
413 and is very efficient and fast to convert to and from.
414
415 The binary format can be stored on disk and interchanged with other applications
416 or computers. fromBinaryData() can be used to convert it back into a
417 JSON document.
418
419 \sa fromBinaryData()
420 */
421QByteArray QJsonDocument::toBinaryData() const
422{
423 if (!d || !d->rawData)
424 return QByteArray();
425
426 return QByteArray(d->rawData, d->header->root()->size + sizeof(QJsonPrivate::Header));
427}
428
429/*!
430 Returns \c true if the document contains an array.
431
432 \sa array(), isObject()
433 */
434bool QJsonDocument::isArray() const
435{
436 if (!d)
437 return false;
438
439 QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData;
440 return h->root()->isArray();
441}
442
443/*!
444 Returns \c true if the document contains an object.
445
446 \sa object(), isArray()
447 */
448bool QJsonDocument::isObject() const
449{
450 if (!d)
451 return false;
452
453 QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData;
454 return h->root()->isObject();
455}
456
457/*!
458 Returns the QJsonObject contained in the document.
459
460 Returns an empty object if the document contains an
461 array.
462
463 \sa isObject(), array(), setObject()
464 */
465QJsonObject QJsonDocument::object() const
466{
467 if (d) {
468 QJsonPrivate::Base *b = d->header->root();
469 if (b->isObject())
470 return QJsonObject(d, static_cast<QJsonPrivate::Object *>(b));
471 }
472 return QJsonObject();
473}
474
475/*!
476 Returns the QJsonArray contained in the document.
477
478 Returns an empty array if the document contains an
479 object.
480
481 \sa isArray(), object(), setArray()
482 */
483QJsonArray QJsonDocument::array() const
484{
485 if (d) {
486 QJsonPrivate::Base *b = d->header->root();
487 if (b->isArray())
488 return QJsonArray(d, static_cast<QJsonPrivate::Array *>(b));
489 }
490 return QJsonArray();
491}
492
493/*!
494 Sets \a object as the main object of this document.
495
496 \sa setArray(), object()
497 */
498void QJsonDocument::setObject(const QJsonObject &object)
499{
500 if (d && !d->ref.deref())
501 delete d;
502
503 d = object.d;
504
505 if (!d) {
506 d = new QJsonPrivate::Data(0, QJsonValue::Object);
507 } else if (d->compactionCounter || object.o != d->header->root()) {
508 QJsonObject o(object);
509 if (d->compactionCounter)
510 o.compact();
511 else
512 o.detach2();
513 d = o.d;
514 d->ref.ref();
515 return;
516 }
517 d->ref.ref();
518}
519
520/*!
521 Sets \a array as the main object of this document.
522
523 \sa setObject(), array()
524 */
525void QJsonDocument::setArray(const QJsonArray &array)
526{
527 if (d && !d->ref.deref())
528 delete d;
529
530 d = array.d;
531
532 if (!d) {
533 d = new QJsonPrivate::Data(0, QJsonValue::Array);
534 } else if (d->compactionCounter || array.a != d->header->root()) {
535 QJsonArray a(array);
536 if (d->compactionCounter)
537 a.compact();
538 else
539 a.detach2();
540 d = a.d;
541 d->ref.ref();
542 return;
543 }
544 d->ref.ref();
545}
546
547#if QT_STRINGVIEW_LEVEL < 2
548/*!
549 Returns a QJsonValue representing the value for the key \a key.
550
551 Equivalent to calling object().value(key).
552
553 The returned QJsonValue is QJsonValue::Undefined if the key does not exist,
554 or if isObject() is false.
555
556 \since 5.10
557
558 \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject
559 */
560const QJsonValue QJsonDocument::operator[](const QString &key) const
561{
562 return (*this)[QStringView(key)];
563}
564#endif
565
566/*!
567 \overload
568 \since 5.14
569*/
570const QJsonValue QJsonDocument::operator[](QStringView key) const
571{
572 if (!isObject())
573 return QJsonValue(QJsonValue::Undefined);
574
575 return object().value(key);
576}
577
578/*!
579 \overload
580 \since 5.10
581*/
582const QJsonValue QJsonDocument::operator[](QLatin1String key) const
583{
584 if (!isObject())
585 return QJsonValue(QJsonValue::Undefined);
586
587 return object().value(key);
588}
589
590/*!
591 Returns a QJsonValue representing the value for index \a i.
592
593 Equivalent to calling array().at(i).
594
595 The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds,
596 or if isArray() is false.
597
598 \since 5.10
599
600 \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray
601 */
602const QJsonValue QJsonDocument::operator[](int i) const
603{
604 if (!isArray())
605 return QJsonValue(QJsonValue::Undefined);
606
607 return array().at(i);
608}
609
610/*!
611 Returns \c true if the \a other document is equal to this document.
612 */
613bool QJsonDocument::operator==(const QJsonDocument &other) const
614{
615 if (d == other.d)
616 return true;
617
618 if (!d || !other.d)
619 return false;
620
621 if (d->header->root()->isArray() != other.d->header->root()->isArray())
622 return false;
623
624 if (d->header->root()->isObject())
625 return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root()))
626 == QJsonObject(other.d, static_cast<QJsonPrivate::Object *>(other.d->header->root()));
627 else
628 return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root()))
629 == QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(other.d->header->root()));
630}
631
632/*!
633 \fn bool QJsonDocument::operator!=(const QJsonDocument &other) const
634
635 returns \c true if \a other is not equal to this document
636 */
637
638/*!
639 returns \c true if this document is null.
640
641 Null documents are documents created through the default constructor.
642
643 Documents created from UTF-8 encoded text or the binary format are
644 validated during parsing. If validation fails, the returned document
645 will also be null.
646 */
647bool QJsonDocument::isNull() const
648{
649 return (d == nullptr);
650}
651
652#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
653QDebug operator<<(QDebug dbg, const QJsonDocument &o)
654{
655 QDebugStateSaver saver(dbg);
656 if (!o.d) {
657 dbg << "QJsonDocument()";
658 return dbg;
659 }
660 QByteArray json;
661 if (o.d->header->root()->isArray())
662 QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(o.d->header->root()), json, 0, true);
663 else
664 QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(o.d->header->root()), json, 0, true);
665 dbg.nospace() << "QJsonDocument("
666 << json.constData() // print as utf-8 string without extra quotation marks
667 << ')';
668 return dbg;
669}
670#endif
671
672#ifndef QT_NO_DATASTREAM
673QDataStream &operator<<(QDataStream &stream, const QJsonDocument &doc)
674{
675 stream << doc.toJson(QJsonDocument::Compact);
676 return stream;
677}
678
679QDataStream &operator>>(QDataStream &stream, QJsonDocument &doc)
680{
681 QByteArray buffer;
682 stream >> buffer;
683 QJsonParseError parseError{};
684 doc = QJsonDocument::fromJson(buffer, &parseError);
685 if (parseError.error && !buffer.isEmpty())
686 stream.setStatus(QDataStream::ReadCorruptData);
687 return stream;
688}
689#endif
690
691QT_END_NAMESPACE
692