summaryrefslogtreecommitdiffstats
path: root/src/corelib/serialization/qjsondocument.cpp
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2019-06-19 13:43:00 +0200
committerUlf Hermann <ulf.hermann@qt.io>2019-10-21 19:12:51 +0200
commit35adb74ddd915831789f0175423660f8e898942e (patch)
tree3ac0fd8be75f0b7e0391149d13aab51a7f02dd3f /src/corelib/serialization/qjsondocument.cpp
parent559b563d711db0760a51b0dce26536dbc8766a9d (diff)
Reimplement JSON support on top of Cbor
In turn, deprecate the QJsonDocument methods that deal with JSON binary data. You should use CBOR for data serialization these days. [ChangeLog][Deprecation Notice] The binary JSON representation is deprecated. The CBOR format should be used instead. Fixes: QTBUG-47629 Change-Id: Ic8b92ea36de87815b12307a9d8b1095f07166db8 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
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
<< ')';