1/****************************************************************************
2**
3** Copyright (C) 2020 Intel Corporation.
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 "qcborvalue.h"
41#include "qcborvalue_p.h"
42
43#include "qcborarray.h"
44#include "qcbormap.h"
45
46#include "qjsonarray.h"
47#include "qjsonobject.h"
48#include "qjsondocument.h"
49#include "qjson_p.h"
50
51#include <private/qnumeric_p.h>
52#include <quuid.h>
53
54QT_BEGIN_NAMESPACE
55
56using namespace QtCbor;
57
58enum class ConversionMode { FromRaw, FromVariantToJson };
59
60static QJsonValue fpToJson(double v)
61{
62 return qt_is_finite(v) ? QJsonValue(v) : QJsonValue();
63}
64
65static QString simpleTypeString(QCborValue::Type t)
66{
67 int simpleType = t - QCborValue::SimpleType;
68 if (unsigned(simpleType) < 0x100)
69 return QString::fromLatin1("simple(%1)").arg(simpleType);
70
71 // if we got here, we got an unknown type
72 qWarning("QCborValue: found unknown type 0x%x", t);
73 return QString();
74
75}
76
77static QString encodeByteArray(const QCborContainerPrivate *d, qsizetype idx, QCborTag encoding)
78{
79 const ByteData *b = d->byteData(idx);
80 if (!b)
81 return QString();
82
83 QByteArray data = QByteArray::fromRawData(b->byte(), b->len);
84 if (encoding == QCborKnownTags::ExpectedBase16)
85 data = data.toHex();
86 else if (encoding == QCborKnownTags::ExpectedBase64)
87 data = data.toBase64();
88 else
89 data = data.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
90
91 return QString::fromLatin1(data, data.size());
92}
93
94static QString makeString(const QCborContainerPrivate *d, qsizetype idx,
95 ConversionMode mode = ConversionMode::FromRaw);
96
97static QString maybeEncodeTag(const QCborContainerPrivate *d)
98{
99 qint64 tag = d->elements.at(0).value;
100 const Element &e = d->elements.at(1);
101 const ByteData *b = d->byteData(e);
102
103 switch (tag) {
104 case qint64(QCborKnownTags::DateTimeString):
105 case qint64(QCborKnownTags::Url):
106 if (e.type == QCborValue::String)
107 return makeString(d, 1);
108 break;
109
110 case qint64(QCborKnownTags::ExpectedBase64url):
111 case qint64(QCborKnownTags::ExpectedBase64):
112 case qint64(QCborKnownTags::ExpectedBase16):
113 if (e.type == QCborValue::ByteArray)
114 return encodeByteArray(d, 1, QCborTag(tag));
115 break;
116
117 case qint64(QCborKnownTags::Uuid):
118 if (e.type == QCborValue::ByteArray && b->len == sizeof(QUuid))
119 return QUuid::fromRfc4122(b->asByteArrayView()).toString(QUuid::WithoutBraces);
120 }
121
122 // don't know what to do, bail out
123 return QString();
124}
125
126static QString encodeTag(const QCborContainerPrivate *d)
127{
128 QString s;
129 if (!d || d->elements.size() != 2)
130 return s; // invalid (incomplete?) tag state
131
132 s = maybeEncodeTag(d);
133 if (s.isNull()) {
134 // conversion failed, ignore the tag and convert the tagged value
135 s = makeString(d, 1);
136 }
137 return s;
138}
139
140static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizetype idx,
141 ConversionMode mode)
142{
143 const auto &e = d->elements.at(idx);
144
145 switch (e.type) {
146 case QCborValue::Integer:
147 return QString::number(qint64(e.value));
148
149 case QCborValue::Double:
150 return QString::number(e.fpvalue());
151
152 case QCborValue::ByteArray:
153 return mode == ConversionMode::FromVariantToJson
154 ? d->stringAt(idx)
155 : encodeByteArray(d, idx, QCborTag(QCborKnownTags::ExpectedBase64url));
156
157 case QCborValue::String:
158 return d->stringAt(idx);
159
160 case QCborValue::Array:
161 case QCborValue::Map:
162#if defined(QT_JSON_READONLY) || defined(QT_BOOTSTRAPPED)
163 qFatal("Writing JSON is disabled.");
164 return QString();
165#else
166 return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact);
167#endif
168
169 case QCborValue::SimpleType:
170 break;
171
172 case QCborValue::False:
173 return QStringLiteral("false");
174
175 case QCborValue::True:
176 return QStringLiteral("true");
177
178 case QCborValue::Null:
179 return QStringLiteral("null");
180
181 case QCborValue::Undefined:
182 return QStringLiteral("undefined");
183
184 case QCborValue::Invalid:
185 return QString();
186
187 case QCborValue::Tag:
188 case QCborValue::DateTime:
189 case QCborValue::Url:
190 case QCborValue::RegularExpression:
191 case QCborValue::Uuid:
192 return encodeTag(e.flags & Element::IsContainer ? e.container : nullptr);
193 }
194
195 // maybe it's a simple type
196 return simpleTypeString(e.type);
197}
198
199QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx,
200 ConversionMode mode = ConversionMode::FromRaw);
201
202static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d)
203{
204#ifndef QT_BUILD_QMAKE
205 qint64 tag = d->elements.at(0).value;
206
207 switch (tag) {
208 case qint64(QCborKnownTags::Url):
209 // use the fullly-encoded URL form
210 if (d->elements.at(1).type == QCborValue::String)
211 return QUrl::fromEncoded(d->byteData(1)->asByteArrayView()).toString(QUrl::FullyEncoded);
212 Q_FALLTHROUGH();
213
214 case qint64(QCborKnownTags::DateTimeString):
215 case qint64(QCborKnownTags::ExpectedBase64url):
216 case qint64(QCborKnownTags::ExpectedBase64):
217 case qint64(QCborKnownTags::ExpectedBase16):
218 case qint64(QCborKnownTags::Uuid): {
219 // use the string conversion
220 QString s = maybeEncodeTag(d);
221 if (!s.isNull())
222 return s;
223 }
224 }
225#endif
226
227 // for all other tags, ignore it and return the converted tagged item
228 return qt_convertToJson(d, 1);
229}
230
231// We need to do this because sub-objects may need conversion.
232static QJsonArray convertToJsonArray(QCborContainerPrivate *d,
233 ConversionMode mode = ConversionMode::FromRaw)
234{
235 QJsonArray a;
236 if (d) {
237 for (qsizetype idx = 0; idx < d->elements.size(); ++idx)
238 a.append(qt_convertToJson(d, idx, mode));
239 }
240 return a;
241}
242
243// We need to do this because the keys need to be sorted and converted to strings
244// and sub-objects may need recursive conversion.
245static QJsonObject convertToJsonObject(QCborContainerPrivate *d,
246 ConversionMode mode = ConversionMode::FromRaw)
247{
248 QJsonObject o;
249 if (d) {
250 for (qsizetype idx = 0; idx < d->elements.size(); idx += 2)
251 o.insert(makeString(d, idx), qt_convertToJson(d, idx + 1, mode));
252 }
253 return o;
254}
255
256QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx, ConversionMode mode)
257{
258 // encoding the container itself
259 if (idx == -QCborValue::Array)
260 return convertToJsonArray(d, mode);
261 if (idx == -QCborValue::Map)
262 return convertToJsonObject(d, mode);
263 if (idx < 0) {
264 // tag-like type
265 if (!d || d->elements.size() != 2)
266 return QJsonValue::Undefined; // invalid state
267 return convertExtendedTypeToJson(d);
268 }
269
270 // an element in the container
271 const auto &e = d->elements.at(idx);
272 switch (e.type) {
273 case QCborValue::Integer:
274 return QJsonValue(e.value);
275 case QCborValue::ByteArray:
276 if (mode == ConversionMode::FromVariantToJson) {
277 const auto value = makeString(d, idx, mode);
278 return value.isEmpty() ? QJsonValue() : QJsonPrivate::Value::fromTrustedCbor(value);
279 }
280 break;
281 case QCborValue::RegularExpression:
282 if (mode == ConversionMode::FromVariantToJson)
283 return QJsonValue();
284 break;
285 case QCborValue::String:
286 case QCborValue::SimpleType:
287 // make string
288 break;
289
290 case QCborValue::Array:
291 case QCborValue::Map:
292 case QCborValue::Tag:
293 case QCborValue::DateTime:
294 case QCborValue::Url:
295 case QCborValue::Uuid:
296 // recurse
297 return qt_convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type,
298 mode);
299
300 case QCborValue::Null:
301 case QCborValue::Undefined:
302 case QCborValue::Invalid:
303 return QJsonValue();
304
305 case QCborValue::False:
306 return false;
307
308 case QCborValue::True:
309 return true;
310
311 case QCborValue::Double:
312 return fpToJson(e.fpvalue());
313 }
314
315 return QJsonPrivate::Value::fromTrustedCbor(makeString(d, idx, mode));
316}
317
318/*!
319 Converts this QCborValue object to an equivalent representation in JSON and
320 returns it as a QJsonValue.
321
322 Please note that CBOR contains a richer and wider type set than JSON, so
323 some information may be lost in this conversion. The following table
324 compares CBOR types to JSON types and indicates whether information may be
325 lost or not.
326
327 \table
328 \header \li CBOR Type \li JSON Type \li Comments
329 \row \li Bool \li Bool \li No data loss possible
330 \row \li Double \li Number \li Infinities and NaN will be converted to Null;
331 no data loss for other values
332 \row \li Integer \li Number \li Data loss possible in the conversion if the
333 integer is larger than 2\sup{53} or smaller
334 than -2\sup{53}.
335 \row \li Null \li Null \li No data loss possible
336 \row \li Undefined \li Null \li Type information lost
337 \row \li String \li String \li No data loss possible
338 \row \li Byte Array \li String \li Converted to a lossless encoding like Base64url,
339 but the distinction between strings and byte
340 arrays is lost
341 \row \li Other simple types \li String \li Type information lost
342 \row \li Array \li Array \li Conversion applies to each contained value
343 \row \li Map \li Object \li Keys are converted to string; values converted
344 according to this table
345 \row \li Tags and extended types \li Special \li The tag number itself is lost and the tagged
346 value is converted to JSON
347 \endtable
348
349 For information on the conversion of CBOR map keys to string, see
350 QCborMap::toJsonObject().
351
352 If this QCborValue contains the undefined value, this function will return
353 an undefined QJsonValue too. Note that JSON does not support undefined
354 values and undefined QJsonValues are an extension to the specification.
355 They cannot be held in a QJsonArray or QJsonObject, but can be returned
356 from functions to indicate a failure. For all other intents and purposes,
357 they are the same as null.
358
359 \section3 Special handling of tags and extended types
360
361 Some tags are handled specially and change the transformation of the tagged
362 value from CBOR to JSON. The following table lists those special cases:
363
364 \table
365 \header \li Tag \li CBOR type \li Transformation
366 \row \li ExpectedBase64url \li Byte array \li Encodes the byte array as Base64url
367 \row \li ExpectedBase64 \li Byte array \li Encodes the byte array as Base64
368 \row \li ExpectedBase16 \li Byte array \li Encodes the byte array as hex
369 \row \li Url \li Url and String \li Uses QUrl::toEncoded() to normalize the
370 encoding to the URL's fully encoded format
371 \row \li Uuid \li Uuid and Byte array \li Uses QUuid::toString() to create
372 the string representation
373 \endtable
374
375 \sa fromJsonValue(), toVariant(), QCborArray::toJsonArray(), QCborMap::toJsonObject()
376 */
377QJsonValue QCborValue::toJsonValue() const
378{
379 if (container)
380 return qt_convertToJson(container, n < 0 ? -type() : n);
381
382 // simple values
383 switch (type()) {
384 case False:
385 return false;
386
387 case Integer:
388 return QJsonPrivate::Value::fromTrustedCbor(*this);
389
390 case True:
391 return true;
392
393 case Null:
394 case Undefined:
395 case Invalid:
396 return QJsonValue();
397
398 case Double:
399 return fpToJson(fp_helper());
400
401 case SimpleType:
402 break;
403
404 case ByteArray:
405 case String:
406 // empty strings
407 return QJsonValue::String;
408
409 case Array:
410 // empty array
411 return QJsonArray();
412
413 case Map:
414 // empty map
415 return QJsonObject();
416
417 case Tag:
418 case DateTime:
419 case Url:
420 case RegularExpression:
421 case Uuid:
422 // Reachable, but invalid in Json
423 return QJsonValue::Undefined;
424 }
425
426 return QJsonPrivate::Value::fromTrustedCbor(simpleTypeString(type()));
427}
428
429QJsonValue QCborValueRef::toJsonValue() const
430{
431 return qt_convertToJson(d, i);
432}
433
434/*!
435 Recursively converts every \l QCborValue element in this array to JSON
436 using QCborValue::toJsonValue() and returns the corresponding QJsonArray
437 composed of those elements.
438
439 Please note that CBOR contains a richer and wider type set than JSON, so
440 some information may be lost in this conversion. For more details on what
441 conversions are applied, see QCborValue::toJsonValue().
442
443 \sa fromJsonArray(), QCborValue::toJsonValue(), QCborMap::toJsonObject(), toVariantList()
444 */
445QJsonArray QCborArray::toJsonArray() const
446{
447 return convertToJsonArray(d.data());
448}
449
450QJsonArray QJsonPrivate::Variant::toJsonArray(const QVariantList &list)
451{
452 const auto cborArray = QCborArray::fromVariantList(list);
453 return convertToJsonArray(cborArray.d.data(), ConversionMode::FromVariantToJson);
454}
455
456/*!
457 Recursively converts every \l QCborValue value in this map to JSON using
458 QCborValue::toJsonValue() and creates a string key for all keys that aren't
459 strings, then returns the corresponding QJsonObject composed of those
460 associations.
461
462 Please note that CBOR contains a richer and wider type set than JSON, so
463 some information may be lost in this conversion. For more details on what
464 conversions are applied, see QCborValue::toJsonValue().
465
466 \section3 Map key conversion to string
467
468 JSON objects are defined as having string keys, unlike CBOR, so the
469 conversion of a QCborMap to QJsonObject will imply a step of
470 "stringification" of the key values. The conversion will use the special
471 handling of tags and extended types from above and will also convert the
472 rest of the types as follows:
473
474 \table
475 \header \li Type \li Transformation
476 \row \li Bool \li "true" and "false"
477 \row \li Null \li "null"
478 \row \li Undefined \li "undefined"
479 \row \li Integer \li The decimal string form of the number
480 \row \li Double \li The decimal string form of the number
481 \row \li Byte array \li Unless tagged differently (see above), encoded as
482 Base64url
483 \row \li Array \li Replaced by the compact form of its
484 \l{QCborValue::toDiagnosticNotation()}{Diagnostic notation}
485 \row \li Map \li Replaced by the compact form of its
486 \l{QCborValue::toDiagnosticNotation()}{Diagnostic notation}
487 \row \li Tags and extended types \li Tag number is dropped and the tagged value is converted
488 to string
489 \endtable
490
491 \sa fromJsonObject(), QCborValue::toJsonValue(), QCborArray::toJsonArray(), toVariantMap()
492 */
493QJsonObject QCborMap::toJsonObject() const
494{
495 return convertToJsonObject(d.data());
496}
497
498QJsonObject QJsonPrivate::Variant::toJsonObject(const QVariantMap &map)
499{
500 const auto cborMap = QCborMap::fromVariantMap(map);
501 return convertToJsonObject(cborMap.d.data(), ConversionMode::FromVariantToJson);
502}
503
504/*!
505 Converts this value to a native Qt type and returns the corresponding QVariant.
506
507 The following table lists the mapping performed between \l{Type}{QCborValue
508 types} and \l{QMetaType::Type}{Qt meta types}.
509
510 \table
511 \header \li CBOR Type \li Qt or C++ type \li Notes
512 \row \li Integer \li \l qint64 \li
513 \row \li Double \li \c double \li
514 \row \li Bool \li \c bool \li
515 \row \li Null \li \c std::nullptr_t \li
516 \row \li Undefined \li no type (QVariant()) \li
517 \row \li Byte array \li \l QByteArray \li
518 \row \li String \li \l QString \li
519 \row \li Array \li \l QVariantList \li Recursively converts all values
520 \row \li Map \li \l QVariantMap \li Key types are "stringified"
521 \row \li Other simple types \li \l QCborSimpleType \li
522 \row \li DateTime \li \l QDateTime \li
523 \row \li Url \li \l QUrl \li
524 \row \li RegularExpression \li \l QRegularExpression \li
525 \row \li Uuid \li \l QUuid \li
526 \row \li Other tags \li Special \li The tag is ignored and the tagged
527 value is converted using this
528 function
529 \endtable
530
531 Note that values in both CBOR Maps and Arrays are converted recursively
532 using this function too and placed in QVariantMap and QVariantList instead.
533 You will not find QCborMap and QCborArray stored inside the QVariants.
534
535 QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
536 to QVariantMap will imply a step of "stringification" of the key values.
537 See QCborMap::toJsonObject() for details.
538
539 \sa fromVariant(), toJsonValue(), QCborArray::toVariantList(), QCborMap::toVariantMap()
540 */
541QVariant QCborValue::toVariant() const
542{
543 switch (type()) {
544 case Integer:
545 return toInteger();
546
547 case Double:
548 return toDouble();
549
550 case SimpleType:
551 break;
552
553 case False:
554 case True:
555 return isTrue();
556
557 case Null:
558 return QVariant::fromValue(nullptr);
559
560 case Undefined:
561 return QVariant();
562
563 case ByteArray:
564 return toByteArray();
565
566 case String:
567 return toString();
568
569 case Array:
570 return toArray().toVariantList();
571
572 case Map:
573 return toMap().toVariantMap();
574
575 case Tag:
576 // ignore tags
577 return taggedValue().toVariant();
578
579 case DateTime:
580 return toDateTime();
581
582#ifndef QT_BOOTSTRAPPED
583 case Url:
584 return toUrl();
585#endif
586
587#if QT_CONFIG(regularexpression)
588 case RegularExpression:
589 return toRegularExpression();
590#endif
591
592 case Uuid:
593 return toUuid();
594
595 case Invalid:
596 return QVariant();
597
598 default:
599 break;
600 }
601
602 if (isSimpleType())
603 return QVariant::fromValue(toSimpleType());
604
605 Q_UNREACHABLE();
606 return QVariant();
607}
608
609/*!
610 Converts the JSON value contained in \a v into its corresponding CBOR value
611 and returns it. There is no data loss in converting from JSON to CBOR, as
612 the CBOR type set is richer than JSON's. Additionally, values converted to
613 CBOR using this function can be converted back to JSON using toJsonValue()
614 with no data loss.
615
616 The following table lists the mapping of JSON types to CBOR types:
617
618 \table
619 \header \li JSON Type \li CBOR Type
620 \row \li Bool \li Bool
621 \row \li Number \li Integer (if the number has no fraction and is in the \l qint64
622 range) or Double
623 \row \li String \li String
624 \row \li Array \li Array
625 \row \li Object \li Map
626 \row \li Null \li Null
627 \endtable
628
629 \l QJsonValue can also be undefined, indicating a previous operation that
630 failed to complete (for example, searching for a key not present in an
631 object). Undefined values are not JSON types and may not appear in JSON
632 arrays and objects, but this function does return the QCborValue undefined
633 value if the corresponding QJsonValue is undefined.
634
635 \sa toJsonValue(), fromVariant(), QCborArray::fromJsonArray(), QCborMap::fromJsonObject()
636 */
637QCborValue QCborValue::fromJsonValue(const QJsonValue &v)
638{
639 switch (v.type()) {
640 case QJsonValue::Bool:
641 return v.toBool();
642 case QJsonValue::Double: {
643 if (v.value.t == Integer)
644 return v.toInteger();
645 return v.toDouble();
646 }
647 case QJsonValue::String:
648 return v.toString();
649 case QJsonValue::Array:
650 return QCborArray::fromJsonArray(v.toArray());
651 case QJsonValue::Object:
652 return QCborMap::fromJsonObject(v.toObject());
653 case QJsonValue::Null:
654 return nullptr;
655 case QJsonValue::Undefined:
656 break;
657 }
658 return QCborValue();
659}
660
661static void appendVariant(QCborContainerPrivate *d, const QVariant &variant)
662{
663 // Handle strings and byte arrays directly, to avoid creating a temporary
664 // dummy container to hold their data.
665 int type = variant.metaType().id();
666 if (type == QMetaType::QString) {
667 d->append(variant.toString());
668 } else if (type == QMetaType::QByteArray) {
669 QByteArray ba = variant.toByteArray();
670 d->appendByteData(ba.constData(), ba.size(), QCborValue::ByteArray);
671 } else {
672 // For everything else, use the function below.
673 d->append(QCborValue::fromVariant(variant));
674 }
675}
676
677/*!
678 Converts the QVariant \a variant into QCborValue and returns it.
679
680 QVariants may contain a large list of different meta types, many of which
681 have no corresponding representation in CBOR. That includes all
682 user-defined meta types. When preparing transmission using CBOR, it is
683 suggested to encode carefully each value to prevent loss of representation.
684
685 The following table lists the conversion this function will apply:
686
687 \table
688 \header \li Qt (C++) type \li CBOR type
689 \row \li invalid (QVariant()) \li Undefined
690 \row \li \c bool \li Bool
691 \row \li \c std::nullptr_t \li Null
692 \row \li \c short, \c ushort, \c int, \c uint, \l qint64 \li Integer
693 \row \li \l quint64 \li Integer, or Double if outside the range of qint64
694 \row \li \c float, \c double \li Double
695 \row \li \l QByteArray \li ByteArray
696 \row \li \l QDateTime \li DateTime
697 \row \li \l QCborSimpleType \li Simple type
698 \row \li \l QJsonArray \li Array, converted using QCborArray::formJsonArray()
699 \row \li \l QJsonDocument \li Array or Map
700 \row \li \l QJsonObject \li Map, converted using QCborMap::fromJsonObject()
701 \row \li \l QJsonValue \li converted using fromJsonValue()
702 \row \li \l QRegularExpression \li RegularExpression
703 \row \li \l QString \li String
704 \row \li \l QStringList \li Array
705 \row \li \l QVariantHash \li Map
706 \row \li \l QVariantList \li Array
707 \row \li \l QVariantMap \li Map
708 \row \li \l QUrl \li Url
709 \row \li \l QUuid \li Uuid
710 \endtable
711
712 If QVariant::isNull() returns true, a null QCborValue is returned or
713 inserted into the list or object, regardless of the type carried by
714 QVariant. Note the behavior change in Qt 6.0 affecting QVariant::isNull()
715 also affects this function.
716
717 For other types not listed above, a conversion to string will be attempted,
718 usually but not always by calling QVariant::toString(). If the conversion
719 fails the value is replaced by an Undefined CBOR value. Note that
720 QVariant::toString() is also lossy for the majority of types.
721
722 Please note that the conversions via QVariant::toString() are subject to
723 change at any time. Both QVariant and QCborValue may be extended in the
724 future to support more types, which will result in a change in how this
725 function performs conversions.
726
727 \sa toVariant(), fromJsonValue(), QCborArray::toVariantList(), QCborMap::toVariantMap(), QJsonValue::fromVariant()
728 */
729QCborValue QCborValue::fromVariant(const QVariant &variant)
730{
731 switch (variant.metaType().id()) {
732 case QMetaType::UnknownType:
733 return {};
734 case QMetaType::Nullptr:
735 return nullptr;
736 case QMetaType::Bool:
737 return variant.toBool();
738 case QMetaType::Short:
739 case QMetaType::UShort:
740 case QMetaType::Int:
741 case QMetaType::LongLong:
742 case QMetaType::UInt:
743 return variant.toLongLong();
744 case QMetaType::ULongLong:
745 if (variant.toULongLong() <= static_cast<uint64_t>(std::numeric_limits<qint64>::max()))
746 return variant.toLongLong();
747 Q_FALLTHROUGH();
748 case QMetaType::Float:
749 case QMetaType::Double:
750 return variant.toDouble();
751 case QMetaType::QString:
752 return variant.toString();
753 case QMetaType::QStringList:
754 return QCborArray::fromStringList(variant.toStringList());
755 case QMetaType::QByteArray:
756 return variant.toByteArray();
757 case QMetaType::QDateTime:
758 return QCborValue(variant.toDateTime());
759#ifndef QT_BOOTSTRAPPED
760 case QMetaType::QUrl:
761 return QCborValue(variant.toUrl());
762#endif
763 case QMetaType::QUuid:
764 return QCborValue(variant.toUuid());
765 case QMetaType::QVariantList:
766 return QCborArray::fromVariantList(variant.toList());
767 case QMetaType::QVariantMap:
768 return QCborMap::fromVariantMap(variant.toMap());
769 case QMetaType::QVariantHash:
770 return QCborMap::fromVariantHash(variant.toHash());
771#ifndef QT_BOOTSTRAPPED
772#if QT_CONFIG(regularexpression)
773 case QMetaType::QRegularExpression:
774 return QCborValue(variant.toRegularExpression());
775#endif
776 case QMetaType::QJsonValue:
777 return fromJsonValue(variant.toJsonValue());
778 case QMetaType::QJsonObject:
779 return QCborMap::fromJsonObject(variant.toJsonObject());
780 case QMetaType::QJsonArray:
781 return QCborArray::fromJsonArray(variant.toJsonArray());
782 case QMetaType::QJsonDocument: {
783 QJsonDocument doc = variant.toJsonDocument();
784 if (doc.isArray())
785 return QCborArray::fromJsonArray(doc.array());
786 return QCborMap::fromJsonObject(doc.object());
787 }
788 case QMetaType::QCborValue:
789 return qvariant_cast<QCborValue>(variant);
790 case QMetaType::QCborArray:
791 return qvariant_cast<QCborArray>(variant);
792 case QMetaType::QCborMap:
793 return qvariant_cast<QCborMap>(variant);
794 case QMetaType::QCborSimpleType:
795 return qvariant_cast<QCborSimpleType>(variant);
796#endif
797 default:
798 break;
799 }
800
801 if (variant.isNull())
802 return QCborValue(nullptr);
803
804 QString string = variant.toString();
805 if (string.isNull())
806 return QCborValue(); // undefined
807 return string;
808}
809
810/*!
811 Recursively converts each \l QCborValue in this array using
812 QCborValue::toVariant() and returns the QVariantList composed of the
813 converted items.
814
815 Conversion to \l QVariant is not completely lossless. Please see the
816 documentation in QCborValue::toVariant() for more information.
817
818 \sa fromVariantList(), fromStringList(), toJsonArray(),
819 QCborValue::toVariant(), QCborMap::toVariantMap()
820 */
821QVariantList QCborArray::toVariantList() const
822{
823 QVariantList retval;
824 retval.reserve(size());
825 for (qsizetype i = 0; i < size(); ++i)
826 retval.append(d->valueAt(i).toVariant());
827 return retval;
828}
829
830/*!
831 Returns a QCborArray containing all the strings found in the \a list list.
832
833 \sa fromVariantList(), fromJsonArray()
834 */
835QCborArray QCborArray::fromStringList(const QStringList &list)
836{
837 QCborArray a;
838 a.detach(list.size());
839 for (const QString &s : list)
840 a.d->append(s);
841 return a;
842}
843
844/*!
845 Converts all the items in the \a list to CBOR using
846 QCborValue::fromVariant() and returns the array composed of those elements.
847
848 Conversion from \l QVariant is not completely lossless. Please see the
849 documentation in QCborValue::fromVariant() for more information.
850
851 \sa toVariantList(), fromStringList(), fromJsonArray(), QCborMap::fromVariantMap()
852 */
853QCborArray QCborArray::fromVariantList(const QVariantList &list)
854{
855 QCborArray a;
856 a.detach(list.size());
857 for (const QVariant &v : list)
858 appendVariant(a.d.data(), v);
859 return a;
860}
861
862/*!
863 Converts all JSON items found in the \a array array to CBOR using
864 QCborValue::fromJson(), and returns the CBOR array composed of those
865 elements.
866
867 This conversion is lossless, as the CBOR type system is a superset of
868 JSON's. Moreover, the array returned by this function can be converted back
869 to the original \a array by using toJsonArray().
870
871 \sa toJsonArray(), toVariantList(), QCborValue::fromJsonValue(), QCborMap::fromJsonObject()
872 */
873QCborArray QCborArray::fromJsonArray(const QJsonArray &array)
874{
875 QCborArray result;
876 result.d = array.a;
877 return result;
878}
879
880/*!
881 Converts the CBOR values to QVariant using QCborValue::toVariant() and
882 "stringifies" all the CBOR keys in this map, returning the QVariantMap that
883 results from that association list.
884
885 QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
886 to QVariantMap will imply a step of "stringification" of the key values.
887 See QCborMap::toJsonObject() for details.
888
889 In addition, the conversion to \l QVariant is not completely lossless.
890 Please see the documentation in QCborValue::toVariant() for more
891 information.
892
893 \sa fromVariantMap(), toVariantHash(), toJsonObject(), QCborValue::toVariant(),
894 QCborArray::toVariantList()
895 */
896QVariantMap QCborMap::toVariantMap() const
897{
898 QVariantMap retval;
899 for (qsizetype i = 0; i < 2 * size(); i += 2)
900 retval.insert(makeString(d.data(), i), d->valueAt(i + 1).toVariant());
901 return retval;
902}
903
904/*!
905 Converts the CBOR values to QVariant using QCborValue::toVariant() and
906 "stringifies" all the CBOR keys in this map, returning the QVariantHash that
907 results from that association list.
908
909 QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
910 to QVariantMap will imply a step of "stringification" of the key values.
911 See QCborMap::toJsonObject() for details.
912
913 In addition, the conversion to \l QVariant is not completely lossless.
914 Please see the documentation in QCborValue::toVariant() for more
915 information.
916
917 \sa fromVariantHash(), toVariantMap(), toJsonObject(), QCborValue::toVariant(),
918 QCborArray::toVariantList()
919 */
920QVariantHash QCborMap::toVariantHash() const
921{
922 QVariantHash retval;
923 retval.reserve(size());
924 for (qsizetype i = 0; i < 2 * size(); i += 2)
925 retval.insert(makeString(d.data(), i), d->valueAt(i + 1).toVariant());
926 return retval;
927}
928
929/*!
930 Converts all the items in \a map to CBOR using QCborValue::fromVariant()
931 and returns the map composed of those elements.
932
933 Conversion from \l QVariant is not completely lossless. Please see the
934 documentation in QCborValue::fromVariant() for more information.
935
936 \sa toVariantMap(), fromVariantHash(), fromJsonObject(), QCborValue::fromVariant()
937 */
938QCborMap QCborMap::fromVariantMap(const QVariantMap &map)
939{
940 QCborMap m;
941 m.detach(map.size());
942 QCborContainerPrivate *d = m.d.data();
943
944 auto it = map.begin();
945 auto end = map.end();
946 for ( ; it != end; ++it) {
947 d->append(it.key());
948 appendVariant(d, it.value());
949 }
950 return m;
951}
952
953/*!
954 Converts all the items in \a hash to CBOR using QCborValue::fromVariant()
955 and returns the map composed of those elements.
956
957 Conversion from \l QVariant is not completely lossless. Please see the
958 documentation in QCborValue::fromVariant() for more information.
959
960 \sa toVariantHash(), fromVariantMap(), fromJsonObject(), QCborValue::fromVariant()
961 */
962QCborMap QCborMap::fromVariantHash(const QVariantHash &hash)
963{
964 QCborMap m;
965 m.detach(hash.size());
966 QCborContainerPrivate *d = m.d.data();
967
968 auto it = hash.begin();
969 auto end = hash.end();
970 for ( ; it != end; ++it) {
971 d->append(it.key());
972 appendVariant(d, it.value());
973 }
974 return m;
975}
976
977/*!
978 Converts all JSON items found in the \a obj object to CBOR using
979 QCborValue::fromJson(), and returns the map composed of those elements.
980
981 This conversion is lossless, as the CBOR type system is a superset of
982 JSON's. Moreover, the map returned by this function can be converted back
983 to the original \a obj by using toJsonObject().
984
985 \sa toJsonObject(), toVariantMap(), QCborValue::fromJsonValue(), QCborArray::fromJsonArray()
986 */
987QCborMap QCborMap::fromJsonObject(const QJsonObject &obj)
988{
989 QCborMap result;
990 result.d = obj.o;
991 return result;
992}
993
994QT_END_NAMESPACE
995