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 | |
57 | QT_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 | |
86 | class QJsonDocumentPrivate |
87 | { |
88 | Q_DISABLE_COPY_MOVE(QJsonDocumentPrivate); |
89 | public: |
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 | */ |
115 | QJsonDocument::QJsonDocument() |
116 | : d(nullptr) |
117 | { |
118 | } |
119 | |
120 | /*! |
121 | * Creates a QJsonDocument from \a object. |
122 | */ |
123 | QJsonDocument::QJsonDocument(const QJsonObject &object) |
124 | : d(nullptr) |
125 | { |
126 | setObject(object); |
127 | } |
128 | |
129 | /*! |
130 | * Constructs a QJsonDocument from \a array. |
131 | */ |
132 | QJsonDocument::QJsonDocument(const QJsonArray &array) |
133 | : d(nullptr) |
134 | { |
135 | setArray(array); |
136 | } |
137 | |
138 | /*! |
139 | \internal |
140 | */ |
141 | QJsonDocument::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 | */ |
152 | QJsonDocument::~QJsonDocument() = default; |
153 | |
154 | /*! |
155 | * Creates a copy of the \a other document. |
156 | */ |
157 | QJsonDocument::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 | |
168 | QJsonDocument::QJsonDocument(QJsonDocument &&other) noexcept |
169 | : d(std::move(other.d)) |
170 | { |
171 | } |
172 | |
173 | void 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 | */ |
182 | QJsonDocument &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 | */ |
227 | QJsonDocument 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 | */ |
259 | QVariant 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) |
291 | QByteArray 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 | */ |
317 | QJsonDocument 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 | */ |
332 | bool 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 | */ |
345 | bool 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 | */ |
358 | bool 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 | */ |
374 | QJsonObject 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 | */ |
391 | QJsonArray 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 | */ |
405 | void 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 | */ |
420 | void 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 | */ |
443 | const 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 | */ |
453 | const 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 | */ |
465 | const 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 | */ |
485 | const 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 | */ |
496 | bool 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 | */ |
518 | bool QJsonDocument::isNull() const |
519 | { |
520 | return (d == nullptr); |
521 | } |
522 | |
523 | #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) |
524 | QDebug 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 |
545 | QDataStream &operator<<(QDataStream &stream, const QJsonDocument &doc) |
546 | { |
547 | stream << doc.toJson(QJsonDocument::Compact); |
548 | return stream; |
549 | } |
550 | |
551 | QDataStream &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 | |
563 | QT_END_NAMESPACE |
564 | |