summaryrefslogtreecommitdiffstats
path: root/src/corelib/serialization/qjsonvalue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/serialization/qjsonvalue.cpp')
-rw-r--r--src/corelib/serialization/qjsonvalue.cpp379
1 files changed, 264 insertions, 115 deletions
diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp
index 34106da231..6c2656d89f 100644
--- a/src/corelib/serialization/qjsonvalue.cpp
+++ b/src/corelib/serialization/qjsonvalue.cpp
@@ -1,41 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qjsonobject.h>
#include <qjsonvalue.h>
@@ -45,6 +10,8 @@
#include <quuid.h>
#include <qvariant.h>
#include <qstringlist.h>
+#include <qmap.h>
+#include <qhash.h>
#include <qdebug.h>
#include "qdatastream.h"
@@ -58,16 +25,44 @@
QT_BEGIN_NAMESPACE
+static QJsonValue::Type convertFromCborType(QCborValue::Type type) noexcept
+{
+ switch (type) {
+ case QCborValue::Null:
+ return QJsonValue::Null;
+ case QCborValue::True:
+ case QCborValue::False:
+ return QJsonValue::Bool;
+ case QCborValue::Double:
+ case QCborValue::Integer:
+ return QJsonValue::Double;
+ case QCborValue::String:
+ return QJsonValue::String;
+ case QCborValue::Array:
+ return QJsonValue::Array;
+ case QCborValue::Map:
+ return QJsonValue::Object;
+ case QCborValue::Undefined:
+ default:
+ return QJsonValue::Undefined;
+ }
+}
+
/*!
\class QJsonValue
\inmodule QtCore
\ingroup json
\ingroup shared
+ \ingroup qtserialization
\reentrant
\since 5.0
\brief The QJsonValue class encapsulates a value in JSON.
+ \compares equality
+ \compareswith equality QJsonValueConstRef QJsonValueRef
+ \endcompareswith
+
A value in JSON can be one of 6 basic types:
JSON is a format to store structured data. It has 6 basic data types:
@@ -105,7 +100,7 @@ QT_BEGIN_NAMESPACE
\li \l {QJsonObject}::operator[](const QString & key) const
\endlist
- \sa {JSON Support in Qt}, {JSON Save Game Example}
+ \sa {JSON Support in Qt}, {Saving and Loading a Game}
*/
/*!
@@ -207,9 +202,9 @@ QJsonValue::QJsonValue(const QString &s)
*/
/*!
- Creates a value of type String, with value \a s.
+ Creates a value of type String, with the Latin-1 string viewed by \a s.
*/
-QJsonValue::QJsonValue(QLatin1String s)
+QJsonValue::QJsonValue(QLatin1StringView s)
: value(s)
{
}
@@ -223,6 +218,15 @@ QJsonValue::QJsonValue(const QJsonArray &a)
}
/*!
+ \overload
+ \since 6.3
+ */
+QJsonValue::QJsonValue(QJsonArray &&a) noexcept
+ : value(QCborArray::fromJsonArray(std::move(a)))
+{
+}
+
+/*!
Creates a value of type Object, with value \a o.
*/
QJsonValue::QJsonValue(const QJsonObject &o)
@@ -230,6 +234,15 @@ QJsonValue::QJsonValue(const QJsonObject &o)
{
}
+/*!
+ \overload
+ \since 6.3
+ */
+QJsonValue::QJsonValue(QJsonObject &&o) noexcept
+ : value(QCborMap::fromJsonObject(std::move(o)))
+{
+}
+
/*!
Destroys the value.
@@ -239,15 +252,12 @@ QJsonValue::~QJsonValue() = default;
/*!
Creates a copy of \a other.
*/
-QJsonValue::QJsonValue(const QJsonValue &other)
- : value(other.value)
-{
-}
+QJsonValue::QJsonValue(const QJsonValue &other) noexcept = default;
/*!
Assigns the value stored in \a other to this object.
*/
-QJsonValue &QJsonValue::operator =(const QJsonValue &other)
+QJsonValue &QJsonValue::operator =(const QJsonValue &other) noexcept
{
QJsonValue copy(other);
swap(copy);
@@ -339,6 +349,7 @@ void QJsonValue::swap(QJsonValue &other) noexcept
error cases as e.g. accessing a non existing key in a QJsonObject.
*/
+#ifndef QT_NO_VARIANT
/*!
Converts \a variant to a QJsonValue and returns it.
@@ -450,7 +461,7 @@ void QJsonValue::swap(QJsonValue &other) noexcept
fails the value is replaced by a null JSON value. Note that
QVariant::toString() is also lossy for the majority of types. For example,
if the passed QVariant is representing raw byte array data, it is recommended
- to pre-encode it to \l {https://www.ietf.org/rfc/rfc4648.txt}{Base64} (or
+ to pre-encode it to \l {RFC 4686}{Base64} (or
another lossless encoding), otherwise a lossy conversion using QString::fromUtf8()
will be used.
@@ -463,7 +474,7 @@ void QJsonValue::swap(QJsonValue &other) noexcept
*/
QJsonValue QJsonValue::fromVariant(const QVariant &variant)
{
- switch (variant.userType()) {
+ switch (variant.metaType().id()) {
case QMetaType::Nullptr:
return QJsonValue(Null);
case QMetaType::Bool:
@@ -581,6 +592,7 @@ QVariant QJsonValue::toVariant() const
error condition, when trying to read an out of bounds value
in an array or a non existent key in an object.
*/
+#endif // !QT_NO_VARIANT
/*!
Returns the type of the value.
@@ -589,25 +601,7 @@ QVariant QJsonValue::toVariant() const
*/
QJsonValue::Type QJsonValue::type() const
{
- switch (value.type()) {
- case QCborValue::Null:
- return QJsonValue::Null;
- case QCborValue::True:
- case QCborValue::False:
- return QJsonValue::Bool;
- case QCborValue::Double:
- case QCborValue::Integer:
- return QJsonValue::Double;
- case QCborValue::String:
- return QJsonValue::String;
- case QCborValue::Array:
- return QJsonValue::Array;
- case QCborValue::Map:
- return QJsonValue::Object;
- case QCborValue::Undefined:
- default:
- return QJsonValue::Undefined;
- }
+ return convertFromCborType(value.type());
}
/*!
@@ -718,11 +712,14 @@ QString QJsonValue::toString() const
*/
QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const
{
- const auto dd = QJsonPrivate::Value::container(value);
- const auto n = QJsonPrivate::Value::valueHelper(value);
- if (value.type() != QCborValue::Array || n >= 0 || !dd)
+ if (!isArray())
return defaultValue;
-
+ QCborContainerPrivate *dd = nullptr;
+ const auto n = QJsonPrivate::Value::valueHelper(value);
+ const auto container = QJsonPrivate::Value::container(value);
+ Q_ASSERT(n == -1 || container == nullptr);
+ if (n < 0)
+ dd = container;
return QJsonArray(dd);
}
@@ -745,11 +742,14 @@ QJsonArray QJsonValue::toArray() const
*/
QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const
{
- const auto dd = QJsonPrivate::Value::container(value);
- const auto n = QJsonPrivate::Value::valueHelper(value);
- if (value.type() != QCborValue::Map || n >= 0 || !dd)
+ if (!isObject())
return defaultValue;
-
+ QCborContainerPrivate *dd = nullptr;
+ const auto container = QJsonPrivate::Value::container(value);
+ const auto n = QJsonPrivate::Value::valueHelper(value);
+ Q_ASSERT(n == -1 || container == nullptr);
+ if (n < 0)
+ dd = container;
return QJsonObject(dd);
}
@@ -765,7 +765,6 @@ QJsonObject QJsonValue::toObject() const
return toObject(QJsonObject());
}
-#if QT_STRINGVIEW_LEVEL < 2
/*!
Returns a QJsonValue representing the value for the key \a key.
@@ -782,7 +781,6 @@ const QJsonValue QJsonValue::operator[](const QString &key) const
{
return (*this)[QStringView(key)];
}
-#endif
/*!
\overload
@@ -800,7 +798,7 @@ const QJsonValue QJsonValue::operator[](QStringView key) const
\overload
\since 5.10
*/
-const QJsonValue QJsonValue::operator[](QLatin1String key) const
+const QJsonValue QJsonValue::operator[](QLatin1StringView key) const
{
if (!isObject())
return QJsonValue(QJsonValue::Undefined);
@@ -829,35 +827,37 @@ const QJsonValue QJsonValue::operator[](qsizetype i) const
}
/*!
- Returns \c true if the value is equal to \a other.
- */
-bool QJsonValue::operator==(const QJsonValue &other) const
+ \fn bool QJsonValue::operator==(const QJsonValue &lhs, const QJsonValue &rhs)
+
+ Returns \c true if the \a lhs value is equal to \a rhs value, \c false otherwise.
+*/
+bool comparesEqual(const QJsonValue &lhs, const QJsonValue &rhs) noexcept
{
- if (value.type() != other.value.type()) {
- if (isDouble() && other.isDouble()) {
+ if (lhs.value.type() != rhs.value.type()) {
+ if (lhs.isDouble() && rhs.isDouble()) {
// One value Cbor integer, one Cbor double, should interact as doubles.
- return toDouble() == other.toDouble();
+ return lhs.toDouble() == rhs.toDouble();
}
return false;
}
- switch (value.type()) {
+ switch (lhs.value.type()) {
case QCborValue::Undefined:
case QCborValue::Null:
case QCborValue::True:
case QCborValue::False:
break;
case QCborValue::Double:
- return toDouble() == other.toDouble();
+ return lhs.toDouble() == rhs.toDouble();
case QCborValue::Integer:
- return QJsonPrivate::Value::valueHelper(value)
- == QJsonPrivate::Value::valueHelper(other.value);
+ return QJsonPrivate::Value::valueHelper(lhs.value)
+ == QJsonPrivate::Value::valueHelper(rhs.value);
case QCborValue::String:
- return toString() == other.toString();
+ return lhs.toString() == rhs.toString();
case QCborValue::Array:
- return toArray() == other.toArray();
+ return lhs.toArray() == rhs.toArray();
case QCborValue::Map:
- return toObject() == other.toObject();
+ return lhs.toObject() == rhs.toObject();
default:
return false;
}
@@ -865,12 +865,10 @@ bool QJsonValue::operator==(const QJsonValue &other) const
}
/*!
- Returns \c true if the value is not equal to \a other.
- */
-bool QJsonValue::operator!=(const QJsonValue &other) const
-{
- return !(*this == other);
-}
+ \fn bool QJsonValue::operator!=(const QJsonValue &lhs, const QJsonValue &rhs)
+
+ Returns \c true if the \a lhs value is not equal to \a rhs value, \c false otherwise.
+*/
/*!
\class QJsonValueRef
@@ -895,48 +893,200 @@ bool QJsonValue::operator!=(const QJsonValue &other) const
However, they are not explicitly documented here.
*/
-
-QJsonValueRef &QJsonValueRef::operator =(const QJsonValue &val)
+void QJsonValueRef::detach()
{
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
+ QCborContainerPrivate *d = QJsonPrivate::Value::container(*this);
+ d = QCborContainerPrivate::detach(d, d->elements.size());
+
if (is_object)
- o->setValueAt(index, val);
+ o->o.reset(d);
else
- a->replace(index, val);
+ a->a.reset(d);
+#else
+ d = QCborContainerPrivate::detach(d, d->elements.size());
+#endif
+}
- return *this;
+static QJsonValueRef &assignToRef(QJsonValueRef &ref, const QCborValue &value, bool is_object)
+{
+ QCborContainerPrivate *d = QJsonPrivate::Value::container(ref);
+ qsizetype index = QJsonPrivate::Value::indexHelper(ref);
+ if (is_object && value.isUndefined()) {
+ d->removeAt(index);
+ d->removeAt(index - 1);
+ } else {
+ d->replaceAt(index, value);
+ }
+
+ return ref;
+}
+
+QJsonValueRef &QJsonValueRef::operator =(const QJsonValue &val)
+{
+ detach();
+ return assignToRef(*this, QCborValue::fromJsonValue(val), is_object);
}
QJsonValueRef &QJsonValueRef::operator =(const QJsonValueRef &ref)
{
- if (is_object)
- o->setValueAt(index, ref);
- else
- a->replace(index, ref);
+ // ### optimize more?
+ const QCborContainerPrivate *d = QJsonPrivate::Value::container(ref);
+ qsizetype index = QJsonPrivate::Value::indexHelper(ref);
- return *this;
+ if (d == QJsonPrivate::Value::container(*this) &&
+ index == QJsonPrivate::Value::indexHelper(*this))
+ return *this; // self assignment
+
+ detach();
+ return assignToRef(*this, d->valueAt(index), is_object);
+}
+
+#ifndef QT_NO_VARIANT
+QVariant QJsonValueConstRef::toVariant() const
+{
+ return concrete(*this).toVariant();
+}
+#endif // !QT_NO_VARIANT
+
+QJsonArray QJsonValueConstRef::toArray() const
+{
+ return concrete(*this).toArray();
+}
+
+QJsonObject QJsonValueConstRef::toObject() const
+{
+ return concrete(*this).toObject();
+}
+
+QJsonValue::Type QJsonValueConstRef::concreteType(QJsonValueConstRef self) noexcept
+{
+ return convertFromCborType(QJsonPrivate::Value::elementHelper(self).type);
+}
+
+bool QJsonValueConstRef::concreteBool(QJsonValueConstRef self, bool defaultValue) noexcept
+{
+ auto &e = QJsonPrivate::Value::elementHelper(self);
+ if (e.type == QCborValue::False)
+ return false;
+ if (e.type == QCborValue::True)
+ return true;
+ return defaultValue;
+}
+
+qint64 QJsonValueConstRef::concreteInt(QJsonValueConstRef self, qint64 defaultValue, bool clamp) noexcept
+{
+ auto &e = QJsonPrivate::Value::elementHelper(self);
+ qint64 v = defaultValue;
+ if (e.type == QCborValue::Double) {
+ // convertDoubleTo modifies the output even on returning false
+ if (!convertDoubleTo<qint64>(e.fpvalue(), &v))
+ v = defaultValue;
+ } else if (e.type == QCborValue::Integer) {
+ v = e.value;
+ }
+ if (clamp && qint64(int(v)) != v)
+ return defaultValue;
+ return v;
+}
+
+double QJsonValueConstRef::concreteDouble(QJsonValueConstRef self, double defaultValue) noexcept
+{
+ auto &e = QJsonPrivate::Value::elementHelper(self);
+ if (e.type == QCborValue::Double)
+ return e.fpvalue();
+ if (e.type == QCborValue::Integer)
+ return e.value;
+ return defaultValue;
+}
+
+QString QJsonValueConstRef::concreteString(QJsonValueConstRef self, const QString &defaultValue)
+{
+ const QCborContainerPrivate *d = QJsonPrivate::Value::container(self);
+ qsizetype index = QJsonPrivate::Value::indexHelper(self);
+ if (d->elements.at(index).type != QCborValue::String)
+ return defaultValue;
+ return d->stringAt(index);
+}
+
+QJsonValue QJsonValueConstRef::concrete(QJsonValueConstRef self) noexcept
+{
+ const QCborContainerPrivate *d = QJsonPrivate::Value::container(self);
+ qsizetype index = QJsonPrivate::Value::indexHelper(self);
+ return QJsonPrivate::Value::fromTrustedCbor(d->valueAt(index));
+}
+
+QString QJsonValueConstRef::objectKey(QJsonValueConstRef self)
+{
+ Q_ASSERT(self.is_object);
+ Q_ASSERT(self.is_object);
+ const QCborContainerPrivate *d = QJsonPrivate::Value::container(self);
+ qsizetype index = QJsonPrivate::Value::indexHelper(self);
+
+ Q_ASSERT(d);
+ Q_ASSERT(index < d->elements.size());
+ return d->stringAt(index - 1);
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
QVariant QJsonValueRef::toVariant() const
{
- return toValue().toVariant();
+ return QJsonValueConstRef::toVariant();
}
QJsonArray QJsonValueRef::toArray() const
{
- return toValue().toArray();
+ return QJsonValueConstRef::toArray();
}
QJsonObject QJsonValueRef::toObject() const
{
- return toValue().toObject();
+ return QJsonValueConstRef::toObject();
}
QJsonValue QJsonValueRef::toValue() const
{
- if (!is_object)
- return a->at(index);
- return o->valueAt(index);
+ return concrete(*this);
}
+#else
+QJsonValueRef QJsonValueRef::operator[](qsizetype key)
+{
+ if (d->elements.at(index).type != QCborValue::Array)
+ d->replaceAt(index, QCborValue::Array);
+
+ auto &e = d->elements[index];
+ e.container = QCborContainerPrivate::grow(e.container, key); // detaches
+ e.flags |= QtCbor::Element::IsContainer;
+
+ return QJsonValueRef(e.container, key, false);
+}
+
+QJsonValueRef QJsonValueRef::operator[](QAnyStringView key)
+{
+ // must go through QJsonObject because some of the machinery is non-static
+ // member or file-static in qjsonobject.cpp
+ QJsonObject o = QJsonPrivate::Value::fromTrustedCbor(d->valueAt(index)).toObject();
+ QJsonValueRef ret = key.visit([&](auto v) {
+ if constexpr (std::is_same_v<decltype(v), QUtf8StringView>)
+ return o[QString::fromUtf8(v)];
+ else
+ return o[v];
+ });
+
+ // ### did the QJsonObject::operator[] above detach?
+ QCborContainerPrivate *x = o.o.take();
+ Q_ASSERT(x->ref.loadRelaxed() == 1);
+
+ auto &e = d->elements[index];
+ if (e.flags & QtCbor::Element::IsContainer && e.container != x)
+ o.o.reset(e.container); // might not an object!
+
+ e.flags |= QtCbor::Element::IsContainer;
+ e.container = x;
+
+ return ret;
+}
+#endif
size_t qHash(const QJsonValue &value, size_t seed)
{
@@ -956,8 +1106,7 @@ size_t qHash(const QJsonValue &value, size_t seed)
case QJsonValue::Undefined:
return seed;
}
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)