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

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