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 | |
21 | QT_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 | |
51 | class QJsonDocumentPrivate |
52 | { |
53 | Q_DISABLE_COPY_MOVE(QJsonDocumentPrivate); |
54 | public: |
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 | */ |
80 | QJsonDocument::QJsonDocument() |
81 | : d(nullptr) |
82 | { |
83 | } |
84 | |
85 | /*! |
86 | * Creates a QJsonDocument from \a object. |
87 | */ |
88 | QJsonDocument::QJsonDocument(const QJsonObject &object) |
89 | : d(nullptr) |
90 | { |
91 | setObject(object); |
92 | } |
93 | |
94 | /*! |
95 | * Constructs a QJsonDocument from \a array. |
96 | */ |
97 | QJsonDocument::QJsonDocument(const QJsonArray &array) |
98 | : d(nullptr) |
99 | { |
100 | setArray(array); |
101 | } |
102 | |
103 | /*! |
104 | \internal |
105 | */ |
106 | QJsonDocument::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 | */ |
117 | QJsonDocument::~QJsonDocument() = default; |
118 | |
119 | /*! |
120 | * Creates a copy of the \a other document. |
121 | */ |
122 | QJsonDocument::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 | |
133 | QJsonDocument::QJsonDocument(QJsonDocument &&other) noexcept |
134 | : d(std::move(other.d)) |
135 | { |
136 | } |
137 | |
138 | void 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 | */ |
147 | QJsonDocument &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 | */ |
192 | QJsonDocument 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 | */ |
224 | QVariant 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) |
256 | QByteArray 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 | */ |
282 | QJsonDocument 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 | */ |
297 | bool 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 | */ |
310 | bool 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 | */ |
323 | bool 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 | */ |
339 | QJsonObject 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 | */ |
356 | QJsonArray 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 | */ |
370 | void 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 | */ |
385 | void 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 | */ |
407 | const QJsonValue QJsonDocument::operator[](const QString &key) const |
408 | { |
409 | return (*this)[QStringView(key)]; |
410 | } |
411 | |
412 | /*! |
413 | \overload |
414 | \since 5.14 |
415 | */ |
416 | const 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 | */ |
428 | const 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 | */ |
448 | const 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 | */ |
459 | bool 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 | */ |
481 | bool QJsonDocument::isNull() const |
482 | { |
483 | return (d == nullptr); |
484 | } |
485 | |
486 | #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) |
487 | QDebug 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 |
508 | QDataStream &operator<<(QDataStream &stream, const QJsonDocument &doc) |
509 | { |
510 | stream << doc.toJson(format: QJsonDocument::Compact); |
511 | return stream; |
512 | } |
513 | |
514 | QDataStream &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 | |
526 | QT_END_NAMESPACE |
527 | |