summaryrefslogtreecommitdiffstats
path: root/src/corelib/serialization/qjsondocument.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/serialization/qjsondocument.cpp')
-rw-r--r--src/corelib/serialization/qjsondocument.cpp354
1 files changed, 205 insertions, 149 deletions
diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp
index 8c3818caff..b9b1902f34 100644
--- a/src/corelib/serialization/qjsondocument.cpp
+++ b/src/corelib/serialization/qjsondocument.cpp
@@ -44,11 +44,22 @@
#include <qstringlist.h>
#include <qvariant.h>
#include <qdebug.h>
+#include <qcbormap.h>
+#include <qcborarray.h>
+#include "qcborvalue_p.h"
#include "qjsonwriter_p.h"
#include "qjsonparser_p.h"
#include "qjson_p.h"
#include "qdatastream.h"
+#if QT_CONFIG(binaryjson)
+#include "qbinaryjson_p.h"
+#include "qbinaryjsonobject_p.h"
+#include "qbinaryjsonarray_p.h"
+#endif
+
+#include <private/qmemory_p.h>
+
QT_BEGIN_NAMESPACE
/*! \class QJsonDocument
@@ -80,6 +91,33 @@ QT_BEGIN_NAMESPACE
\sa {JSON Support in Qt}, {JSON Save Game Example}
*/
+
+class QJsonDocumentPrivate
+{
+ Q_DISABLE_COPY_MOVE(QJsonDocumentPrivate);
+public:
+ QJsonDocumentPrivate() = default;
+ QJsonDocumentPrivate(QCborValue data) : value(std::move(data)) {}
+ ~QJsonDocumentPrivate()
+ {
+ if (rawData)
+ free(rawData);
+ }
+
+ QCborValue value;
+ char *rawData = nullptr;
+ uint rawDataSize = 0;
+
+ void clearRawData()
+ {
+ if (rawData) {
+ free(rawData);
+ rawData = nullptr;
+ rawDataSize = 0;
+ }
+ }
+};
+
/*!
* Constructs an empty and invalid document.
*/
@@ -109,11 +147,10 @@ QJsonDocument::QJsonDocument(const QJsonArray &array)
/*!
\internal
*/
-QJsonDocument::QJsonDocument(QJsonPrivate::Data *data)
- : d(data)
+QJsonDocument::QJsonDocument(const QCborValue &data)
+ : d(qt_make_unique<QJsonDocumentPrivate>(data))
{
Q_ASSERT(d);
- d->ref.ref();
}
/*!
@@ -121,20 +158,30 @@ QJsonDocument::QJsonDocument(QJsonPrivate::Data *data)
Binary data set with fromRawData is not freed.
*/
-QJsonDocument::~QJsonDocument()
-{
- if (d && !d->ref.deref())
- delete d;
-}
+QJsonDocument::~QJsonDocument() = default;
/*!
* Creates a copy of the \a other document.
*/
QJsonDocument::QJsonDocument(const QJsonDocument &other)
{
- d = other.d;
- if (d)
- d->ref.ref();
+ if (other.d) {
+ if (!d)
+ d = qt_make_unique<QJsonDocumentPrivate>();
+ d->value = other.d->value;
+ } else {
+ d.reset();
+ }
+}
+
+QJsonDocument::QJsonDocument(QJsonDocument &&other) noexcept
+ : d(std::move(other.d))
+{
+}
+
+void QJsonDocument::swap(QJsonDocument &other) noexcept
+{
+ qSwap(d, other.d);
}
/*!
@@ -143,14 +190,17 @@ QJsonDocument::QJsonDocument(const QJsonDocument &other)
*/
QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
{
- if (d != other.d) {
- if (d && !d->ref.deref())
- delete d;
- d = other.d;
- if (d)
- d->ref.ref();
+ if (this != &other) {
+ if (other.d) {
+ if (!d)
+ d = qt_make_unique<QJsonDocumentPrivate>();
+ else
+ d->clearRawData();
+ d->value = other.d->value;
+ } else {
+ d.reset();
+ }
}
-
return *this;
}
@@ -187,12 +237,13 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
the application.
*/
+#if QT_CONFIG(binaryjson)
/*!
Creates a QJsonDocument that uses the first \a size bytes from
\a data. It assumes \a data contains a binary encoded JSON document.
- The created document does not take ownership of \a data and the caller
- has to guarantee that \a data will not be deleted or modified as long as
- any QJsonDocument, QJsonObject or QJsonArray still references the data.
+ The created document does not take ownership of \a data. The data is
+ copied into a different data structure, and the original data can be
+ deleted or modified afterwards.
\a data has to be aligned to a 4 byte boundary.
@@ -202,7 +253,18 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
Returns a QJsonDocument representing the data.
- \sa rawData(), fromBinaryData(), isNull(), DataValidation
+ \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
+ compatibility. It is undocumented and restrictive in the maximum size of JSON
+ documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
+ which can in turn be serialized into the CBOR binary format and vice versa. The
+ CBOR format is a well-defined and less restrictive binary representation for a
+ superset of JSON.
+
+ \note Before Qt 5.15, the caller had to guarantee that \a data would not be
+ deleted or modified as long as any QJsonDocument, QJsonObject or QJsonArray
+ still referenced the data. From Qt 5.15 on, this is not necessary anymore.
+
+ \sa rawData(), fromBinaryData(), isNull(), DataValidation, QCborValue
*/
QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation)
{
@@ -211,18 +273,15 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat
return QJsonDocument();
}
- if (size < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)))
+ if (size < 0 || uint(size) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base))
return QJsonDocument();
- QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size);
- d->ownsData = false;
+ std::unique_ptr<QBinaryJsonPrivate::ConstData> binaryData
+ = qt_make_unique<QBinaryJsonPrivate::ConstData>(data, size);
- if (validation != BypassValidation && !d->valid()) {
- delete d;
- return QJsonDocument();
- }
-
- return QJsonDocument(d);
+ return (validation == BypassValidation || binaryData->isValid())
+ ? binaryData->toJsonDocument()
+ : QJsonDocument();
}
/*!
@@ -230,7 +289,16 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat
\a size will contain the size of the returned data.
This method is useful to e.g. stream the JSON document
- in it's binary form to a file.
+ in its binary form to a file.
+
+ \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
+ compatibility. It is undocumented and restrictive in the maximum size of JSON
+ documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
+ which can in turn be serialized into the CBOR binary format and vice versa. The
+ CBOR format is a well-defined and less restrictive binary representation for a
+ superset of JSON.
+
+ \sa QCborValue
*/
const char *QJsonDocument::rawData(int *size) const
{
@@ -238,7 +306,21 @@ const char *QJsonDocument::rawData(int *size) const
*size = 0;
return nullptr;
}
- *size = d->alloc;
+
+ if (!d->rawData) {
+ if (isObject()) {
+ QBinaryJsonObject o = QBinaryJsonObject::fromJsonObject(object());
+ d->rawData = o.takeRawData(&(d->rawDataSize));
+ } else {
+ QBinaryJsonArray a = QBinaryJsonArray::fromJsonArray(array());
+ d->rawData = a.takeRawData(&(d->rawDataSize));
+ }
+ }
+
+ // It would be quite miraculous if not, as we should have hit the 128MB limit then.
+ Q_ASSERT(d->rawDataSize <= uint(std::numeric_limits<int>::max()));
+
+ *size = d->rawDataSize;
return d->rawData;
}
@@ -249,38 +331,64 @@ const char *QJsonDocument::rawData(int *size) const
By default the data is validated. If the \a data is not valid, the method returns
a null document.
- \sa toBinaryData(), fromRawData(), isNull(), DataValidation
+ \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
+ compatibility. It is undocumented and restrictive in the maximum size of JSON
+ documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
+ which can in turn be serialized into the CBOR binary format and vice versa. The
+ CBOR format is a well-defined and less restrictive binary representation for a
+ superset of JSON.
+
+ \sa toBinaryData(), fromRawData(), isNull(), DataValidation, QCborValue
*/
QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation)
{
- if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)))
+ if (uint(data.size()) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base))
return QJsonDocument();
- QJsonPrivate::Header h;
- memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header));
- QJsonPrivate::Base root;
- memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base));
+ QBinaryJsonPrivate::Header h;
+ memcpy(&h, data.constData(), sizeof(QBinaryJsonPrivate::Header));
+ QBinaryJsonPrivate::Base root;
+ memcpy(&root, data.constData() + sizeof(QBinaryJsonPrivate::Header),
+ sizeof(QBinaryJsonPrivate::Base));
- // do basic checks here, so we don't try to allocate more memory than we can.
- if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u ||
- sizeof(QJsonPrivate::Header) + root.size > (uint)data.size())
+ const uint size = sizeof(QBinaryJsonPrivate::Header) + root.size;
+ if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1U || size > uint(data.size()))
return QJsonDocument();
- const uint size = sizeof(QJsonPrivate::Header) + root.size;
- char *raw = (char *)malloc(size);
- if (!raw)
- return QJsonDocument();
+ std::unique_ptr<QBinaryJsonPrivate::ConstData> d
+ = qt_make_unique<QBinaryJsonPrivate::ConstData>(data.constData(), size);
- memcpy(raw, data.constData(), size);
- QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size);
+ return (validation == BypassValidation || d->isValid())
+ ? d->toJsonDocument()
+ : QJsonDocument();
+}
- if (validation != BypassValidation && !d->valid()) {
- delete d;
- return QJsonDocument();
- }
+/*!
+ Returns a binary representation of the document.
+
+ The binary representation is also the native format used internally in Qt,
+ and is very efficient and fast to convert to and from.
+
+ The binary format can be stored on disk and interchanged with other applications
+ or computers. fromBinaryData() can be used to convert it back into a
+ JSON document.
+
+ \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards
+ compatibility. It is undocumented and restrictive in the maximum size of JSON
+ documents that can be encoded. Qt JSON types can be converted to Qt CBOR types,
+ which can in turn be serialized into the CBOR binary format and vice versa. The
+ CBOR format is a well-defined and less restrictive binary representation for a
+ superset of JSON.
- return QJsonDocument(d);
+ \sa fromBinaryData(), QCborValue
+ */
+QByteArray QJsonDocument::toBinaryData() const
+{
+ int size = 0;
+ const char *raw = rawData(&size);
+ return QByteArray(raw, size);
}
+#endif // QT_CONFIG(binaryjson)
/*!
Creates a QJsonDocument from the QVariant \a variant.
@@ -293,6 +401,7 @@ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidati
QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
{
QJsonDocument doc;
+
switch (variant.type()) {
case QVariant::Map:
doc.setObject(QJsonObject::fromVariantMap(variant.toMap()));
@@ -304,7 +413,8 @@ QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
doc.setArray(QJsonArray::fromVariantList(variant.toList()));
break;
case QVariant::StringList:
- doc.setArray(QJsonArray::fromStringList(variant.toStringList()));
+ doc.d = qt_make_unique<QJsonDocumentPrivate>();
+ doc.d->value = QCborArray::fromStringList(variant.toStringList());
break;
default:
break;
@@ -325,10 +435,10 @@ QVariant QJsonDocument::toVariant() const
if (!d)
return QVariant();
- if (d->header->root()->isArray())
- return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root())).toVariantList();
- else
- return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root())).toVariantMap();
+ QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
+ if (d->value.isArray())
+ return QJsonArray(container).toVariantList();
+ return QJsonObject(container).toVariantMap();
}
/*!
@@ -370,10 +480,11 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const
if (!d)
return json;
- if (d->header->root()->isArray())
- QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(d->header->root()), json, 0, (format == Compact));
+ const QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value);
+ if (d->value.isArray())
+ QJsonPrivate::Writer::arrayToJson(container, json, 0, (format == Compact));
else
- QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(d->header->root()), json, 0, (format == Compact));
+ QJsonPrivate::Writer::objectToJson(container, json, 0, (format == Compact));
return json;
}
@@ -392,7 +503,13 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const
QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error)
{
QJsonPrivate::Parser parser(json.constData(), json.length());
- return parser.parse(error);
+ QJsonDocument result;
+ const QCborValue val = parser.parse(error);
+ if (val.isArray() || val.isMap()) {
+ result.d = qt_make_unique<QJsonDocumentPrivate>();
+ result.d->value = val;
+ }
+ return result;
}
/*!
@@ -407,26 +524,6 @@ bool QJsonDocument::isEmpty() const
}
/*!
- Returns a binary representation of the document.
-
- The binary representation is also the native format used internally in Qt,
- and is very efficient and fast to convert to and from.
-
- The binary format can be stored on disk and interchanged with other applications
- or computers. fromBinaryData() can be used to convert it back into a
- JSON document.
-
- \sa fromBinaryData()
- */
-QByteArray QJsonDocument::toBinaryData() const
-{
- if (!d || !d->rawData)
- return QByteArray();
-
- return QByteArray(d->rawData, d->header->root()->size + sizeof(QJsonPrivate::Header));
-}
-
-/*!
Returns \c true if the document contains an array.
\sa array(), isObject()
@@ -436,8 +533,7 @@ bool QJsonDocument::isArray() const
if (!d)
return false;
- QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData;
- return h->root()->isArray();
+ return d->value.isArray();
}
/*!
@@ -450,8 +546,7 @@ bool QJsonDocument::isObject() const
if (!d)
return false;
- QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData;
- return h->root()->isObject();
+ return d->value.isMap();
}
/*!
@@ -464,10 +559,9 @@ bool QJsonDocument::isObject() const
*/
QJsonObject QJsonDocument::object() const
{
- if (d) {
- QJsonPrivate::Base *b = d->header->root();
- if (b->isObject())
- return QJsonObject(d, static_cast<QJsonPrivate::Object *>(b));
+ if (isObject()) {
+ if (auto container = QJsonPrivate::Value::container(d->value))
+ return QJsonObject(container);
}
return QJsonObject();
}
@@ -482,10 +576,9 @@ QJsonObject QJsonDocument::object() const
*/
QJsonArray QJsonDocument::array() const
{
- if (d) {
- QJsonPrivate::Base *b = d->header->root();
- if (b->isArray())
- return QJsonArray(d, static_cast<QJsonPrivate::Array *>(b));
+ if (isArray()) {
+ if (auto container = QJsonPrivate::Value::container(d->value))
+ return QJsonArray(container);
}
return QJsonArray();
}
@@ -497,24 +590,12 @@ QJsonArray QJsonDocument::array() const
*/
void QJsonDocument::setObject(const QJsonObject &object)
{
- if (d && !d->ref.deref())
- delete d;
-
- d = object.d;
+ if (!d)
+ d = qt_make_unique<QJsonDocumentPrivate>();
+ else
+ d->clearRawData();
- if (!d) {
- d = new QJsonPrivate::Data(0, QJsonValue::Object);
- } else if (d->compactionCounter || object.o != d->header->root()) {
- QJsonObject o(object);
- if (d->compactionCounter)
- o.compact();
- else
- o.detach2();
- d = o.d;
- d->ref.ref();
- return;
- }
- d->ref.ref();
+ d->value = QCborValue::fromJsonValue(object);
}
/*!
@@ -524,24 +605,12 @@ void QJsonDocument::setObject(const QJsonObject &object)
*/
void QJsonDocument::setArray(const QJsonArray &array)
{
- if (d && !d->ref.deref())
- delete d;
-
- d = array.d;
+ if (!d)
+ d = qt_make_unique<QJsonDocumentPrivate>();
+ else
+ d->clearRawData();
- if (!d) {
- d = new QJsonPrivate::Data(0, QJsonValue::Array);
- } else if (d->compactionCounter || array.a != d->header->root()) {
- QJsonArray a(array);
- if (d->compactionCounter)
- a.compact();
- else
- a.detach2();
- d = a.d;
- d->ref.ref();
- return;
- }
- d->ref.ref();
+ d->value = QCborValue::fromJsonValue(array);
}
#if QT_STRINGVIEW_LEVEL < 2
@@ -572,7 +641,7 @@ const QJsonValue QJsonDocument::operator[](QStringView key) const
if (!isObject())
return QJsonValue(QJsonValue::Undefined);
- return object().value(key);
+ return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key));
}
/*!
@@ -584,7 +653,7 @@ const QJsonValue QJsonDocument::operator[](QLatin1String key) const
if (!isObject())
return QJsonValue(QJsonValue::Undefined);
- return object().value(key);
+ return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key));
}
/*!
@@ -604,7 +673,7 @@ const QJsonValue QJsonDocument::operator[](int i) const
if (!isArray())
return QJsonValue(QJsonValue::Undefined);
- return array().at(i);
+ return QJsonPrivate::Value::fromTrustedCbor(d->value.toArray().at(i));
}
/*!
@@ -612,21 +681,7 @@ const QJsonValue QJsonDocument::operator[](int i) const
*/
bool QJsonDocument::operator==(const QJsonDocument &other) const
{
- if (d == other.d)
- return true;
-
- if (!d || !other.d)
- return false;
-
- if (d->header->root()->isArray() != other.d->header->root()->isArray())
- return false;
-
- if (d->header->root()->isObject())
- return QJsonObject(d, static_cast<QJsonPrivate::Object *>(d->header->root()))
- == QJsonObject(other.d, static_cast<QJsonPrivate::Object *>(other.d->header->root()));
- else
- return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root()))
- == QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(other.d->header->root()));
+ return (!d) ? (!other.d) : (d->value == other.d->value);
}
/*!
@@ -658,10 +713,11 @@ QDebug operator<<(QDebug dbg, const QJsonDocument &o)
return dbg;
}
QByteArray json;
- if (o.d->header->root()->isArray())
- QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(o.d->header->root()), json, 0, true);
+ const QCborContainerPrivate *container = QJsonPrivate::Value::container(o.d->value);
+ if (o.d->value.isArray())
+ QJsonPrivate::Writer::arrayToJson(container, json, 0, true);
else
- QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(o.d->header->root()), json, 0, true);
+ QJsonPrivate::Writer::objectToJson(container, json, 0, true);
dbg.nospace() << "QJsonDocument("
<< json.constData() // print as utf-8 string without extra quotation marks
<< ')';