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

source code of qtbase/src/corelib/serialization/qjsondocument.cpp