diff options
Diffstat (limited to 'src/corelib/json/qjsondocument.cpp')
-rw-r--r-- | src/corelib/json/qjsondocument.cpp | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/src/corelib/json/qjsondocument.cpp b/src/corelib/json/qjsondocument.cpp new file mode 100644 index 0000000000..e76756da69 --- /dev/null +++ b/src/corelib/json/qjsondocument.cpp @@ -0,0 +1,536 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qjsondocument.h> +#include <qjsonobject.h> +#include <qjsonvalue.h> +#include <qjsonarray.h> +#include <qstringlist.h> +#include <qdebug.h> +#include "qjsonwriter_p.h" +#include "qjsonparser_p.h" +#include "qjson_p.h" + +QT_BEGIN_NAMESPACE + +/*! \class QJsonDocument + \ingroup json + \reentrant + \since 5.0 + + \brief The QJsonDocument class provides a way to read and write JSON documents. + + QJsonDocument is a class that wraps a complete JSON document and can read and + write this document both from a UTF-8 encoded text based representation as well + as Qt's own binary format. + + A JSON document can be converted from its text-based representation to a QJsonDocument + using QJsonDocument::fromJson(). toJson() converts it back to text. The parser is very + fast and efficient and converts the JSON to the binary representation used by Qt. + + Validity of the parsed document can be queried with !isNull() + + A document can be queried as to whether it contains an array or an object using isArray() + and isObject(). The array or object contained in the document can be retrieved using + array() or object() and then read or manipulated. + + A document can also be created from a stored binary representation using fromBinaryData() or + fromRawData(). +*/ + +/*! + * Constructs an empty and invalid document. + */ +QJsonDocument::QJsonDocument() + : d(0) +{ +} + +/*! + * Creates a QJsonDocument from \a object. + */ +QJsonDocument::QJsonDocument(const QJsonObject &object) + : d(0) +{ + setObject(object); +} + +/*! + * Constructs a QJsonDocument from \a array. + */ +QJsonDocument::QJsonDocument(const QJsonArray &array) + : d(0) +{ + setArray(array); +} + +/*! \internal + */ +QJsonDocument::QJsonDocument(QJsonPrivate::Data *data) + : d(data) +{ + Q_ASSERT(d); + d->ref.ref(); +} + +/*! + Deletes the document. + + Binary data set with fromRawData is not freed. + */ +QJsonDocument::~QJsonDocument() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + * Creates a copy of the \a other document. + */ +QJsonDocument::QJsonDocument(const QJsonDocument &other) +{ + d = other.d; + if (d) + d->ref.ref(); +} + +/*! + * Assigns the \a other document to this QJsonDocument. + * Returns a reference to this object. + */ +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(); + } + + return *this; +} + +/*! \enum QJsonDocument::DataValidation + + This value is used to tell QJsonDocument whether to validate the binary data + when converting to a QJsonDocument using fromBinaryData() or fromRawData(). + + \value Validate Validate the data before using it. This is the default. + \value BypassValidation Bypasses data validation. Only use if you received the + data from a trusted place and know it's valid, as using of invalid data can crash + the application. + */ + +/*! + 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. + + \a data has to be aligned to a 4 byte boundary. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + Returns a QJsonDocument representing the data. + + \sa rawData fromBinaryData isNull DataValidation + */ +QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) +{ + if (!(((quintptr)validation) & ~3)) { + qWarning() <<"QJsonDocumnt::fromRawData: data has to have 4 byte alignment"; + return QJsonDocument(); + } + + QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size); + d->ownsData = false; + + if (validation != BypassValidation && !d->valid()) { + delete d; + return QJsonDocument(); + } + + return QJsonDocument(d); +} + +/*! + Returns the raw binary representation of the data + \a size will contain the size of the \a data. + + This method is useful to e.g. stream the JSON document + in it's binary form to a file. + */ +const char *QJsonDocument::rawData(int *size) const +{ + if (!d) { + *size = 0; + return 0; + } + *size = d->alloc; + return d->rawData; +} + +/*! + Creates a QJsonDocument from \a data. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + \sa toBinaryData fromRawData isNull DataValidation + */ +QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) +{ + QJsonPrivate::Header h; + memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header)); + QJsonPrivate::Base root; + memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base)); + + // do basic checks here, so we don't try to allocate more memory than we can. + if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)) || + h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u || + sizeof(QJsonPrivate::Header) + root.size > (uint)data.size()) + return QJsonDocument(); + + char *raw = (char *)malloc(data.size()); + if (!raw) + return QJsonDocument(); + + memcpy(raw, data.constData(), data.size()); + QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, data.size()); + + if (validation != BypassValidation && !d->valid()) { + delete d; + return QJsonDocument(); + } + + return QJsonDocument(d); +} + +/*! + Creates a QJsonDocument from the QVariant \a variant. + + If the \a variant contains any other type than a QVariant::Map, + QVariant::List or QVariant::StringList, the returned document + document is invalid. + + \sa toVariant + */ +QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) +{ + QJsonDocument doc; + if (variant.type() == QVariant::Map) { + doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); + } else if (variant.type() == QVariant::List) { + doc.setArray(QJsonArray::fromVariantList(variant.toList())); + } else if (variant.type() == QVariant::StringList) { + doc.setArray(QJsonArray::fromStringList(variant.toStringList())); + } + return doc; +} + +/*! + Returns a QVariant representing the Json document. + + The returned variant will be a QVariantList if the document is + a QJsonArray and a QVariantMap if the document is a QJsonObject. + + \sa fromVariant, QJsonValue::toVariant() + */ +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(); +} + +/*! + Converts the QJsonDocument to a UTF-8 encoded JSON document. + + \sa fromJson + */ +QByteArray QJsonDocument::toJson() const +{ + if (!d) + return QByteArray(); + + QByteArray json; + + if (d->header->root()->isArray()) + QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(d->header->root()), json, 0); + else + QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(d->header->root()), json, 0); + + return json; +} + +/*! + Parses a UTF-8 encoded JSON document and creates a QJsonDocument + from it. isNull() will return \c false if no error was encountered during + parsing. + + \sa toJson + */ +QJsonDocument QJsonDocument::fromJson(const QByteArray &json) +{ + QJsonPrivate::Parser parser(json.constData(), json.length()); + return parser.parse(); +} + +/*! + Returns true if the document doesn't contain any data. + */ +bool QJsonDocument::isEmpty() const +{ + if (!d) + return true; + + return false; +} + +/*! + 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 true if the document contains an array. + + \sa array() isObject() + */ +bool QJsonDocument::isArray() const +{ + if (!d) + return false; + + QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; + return h->root()->isArray(); +} + +/*! + Returns true if the document contains an object. + + \sa object() isArray() + */ +bool QJsonDocument::isObject() const +{ + if (!d) + return false; + + QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; + return h->root()->isObject(); +} + +/*! + Returns the QJsonObject contained in the document. + + Returns an empty object if the document contains an + array. + + \sa isObject array setObject + */ +QJsonObject QJsonDocument::object() const +{ + if (d) { + QJsonPrivate::Base *b = d->header->root(); + if (b->isObject()) + return QJsonObject(d, static_cast<QJsonPrivate::Object *>(b)); + } + return QJsonObject(); +} + +/*! + Returns the QJsonArray contained in the document. + + Returns an empty array if the document contains an + object. + + \sa isArray object setArray + */ +QJsonArray QJsonDocument::array() const +{ + if (d) { + QJsonPrivate::Base *b = d->header->root(); + if (b->isArray()) + return QJsonArray(d, static_cast<QJsonPrivate::Array *>(b)); + } + return QJsonArray(); +} + +/*! + Sets \a object as the main object of this document. + + \sa setArray object + */ +void QJsonDocument::setObject(const QJsonObject &object) +{ + if (d && !d->ref.deref()) + delete d; + + d = object.d; + + 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.detach(); + d = o.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Sets \a array as the main object of this document. + + \sa setObject array + */ +void QJsonDocument::setArray(const QJsonArray &array) +{ + if (d && !d->ref.deref()) + delete d; + + d = array.d; + + 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.detach(); + d = a.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Returns \c true if the \a other document is equal to this document. + */ +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())); +} + +/*! + \fn bool QJsonDocument::operator!=(const QJsonDocument &other) const + + returns \c true if \a other is not equal to this document + */ + +/*! + returns true if this document is null. + + Null documents are documents created through the default constructor. + + Documents created from UTF-8 encoded text or the binary format are + validated during parsing. If validation fails, the returned document + will also be null. + */ +bool QJsonDocument::isNull() const +{ + return (d == 0); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QJsonDocument &o) +{ + if (!o.d) { + dbg << "QJsonDocument()"; + return dbg; + } + QByteArray json; + if (o.d->header->root()->isArray()) + QJsonPrivate::Writer::arrayToJson(static_cast<QJsonPrivate::Array *>(o.d->header->root()), json, 0, true); + else + QJsonPrivate::Writer::objectToJson(static_cast<QJsonPrivate::Object *>(o.d->header->root()), json, 0, true); + dbg.nospace() << "QJsonDocument(" + << json.constData() // print as utf-8 string without extra quotation marks + << ")"; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE |