/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include namespace QtJson { /*! \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 it's 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 isValid() 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(). In this case the document is not automatically checked for validity. If the document comes from an untrusted source isValid() should therefore be called before using the document any further. isValid() will guarantee that the binary document is in a state that will not cause problems when using it. */ /*! * 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(Private::Data *data) : d(data) { Q_ASSERT(d); d->ref.ref(); } /*! * Deletes the document. */ 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; } /*! 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. No validity checks on \a data are performed. If you are unsure about the validity of the binary data, call isValid \returns a QJsonDocument representing the data \sa rawData fromBinaryData isValid */ QJsonDocument QJsonDocument::fromRawData(const char *data, int size) { Private::Data *d = new Private::Data((char *)data, size); d->ownsData = false; 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. The data is expected to be valid, call isValid() if the data comes from an untrusted source before using it. \sa toBinaryData fromRawData isValid */ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data) { Private::Header *h = (Private::Header *) data.constData(); if (data.size() < (int)(sizeof(Private::Header) + sizeof(Private::Base)) || h->tag != QJsonDocument::BinaryFormatTag || h->version != 1u || sizeof(Private::Header) + h->root()->size > (uint)data.size()) return QJsonDocument(); char *raw = (char *)malloc(data.size()); memcpy(raw, data.constData(), data.size()); Private::Data *d = new Private::Data(raw, data.size()); return QJsonDocument(d); } /*! Creates a QJsonDocument from the 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 JSON data types are mappes as follows: \list \o Null an empty variant \o Bool QVariant::Bool \o Double QVariant::Double \o String QVariant::String \o Array QVariant::List \o Object QVariant::Map \endlist \sa fromVariant. */ QVariant QJsonDocument::toVariant() const { if (!d) return QVariant(); if (d->header->root()->isArray()) return QJsonArray(d, static_cast(d->header->root())).toVariantList(); else return QJsonObject(d, static_cast(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()) QJsonWriter::arrayToJson(static_cast(d->header->root()), json, 0); else QJsonWriter::objectToJson(static_cast(d->header->root()), json, 0); return json; } /*! Parses a utf-8 encoded JSON document and creates a QJsonDocument from it. isValid will return true if no error was encountered during parsing. \sa toJson */ QJsonDocument QJsonDocument::fromJson(const QByteArray &json) { QJsonParser 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 such 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(Private::Header)); } /*! \returns true if the document contains an array. \sa array() isObject() */ bool QJsonDocument::isArray() const { if (!d) return false; Private::Header *h = (Private::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; Private::Header *h = (Private::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) { Private::Base *b = d->header->root(); if (b->isObject()) return QJsonObject(d, static_cast(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) { Private::Base *b = d->header->root(); if (b->isArray()) return QJsonArray(d, static_cast(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 Private::Data(0, QJsonValue::Object); } else if (d->compactionCounter) { object.compact(); d = object.d; } else if (object.o != d->header->root()) { QJsonObject detached(object); detached.detach(); d = detached.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 Private::Data(0, QJsonValue::Array); } else if (d->compactionCounter) { array.compact(); d = array.d; } else if (array.a != d->header->root()) { QJsonArray detached(array); detached.detach(); d = detached.d; d->ref.ref(); return; } d->ref.ref(); } /*! returns true if \a other 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(d->header->root())) == QJsonObject(other.d, static_cast(other.d->header->root())); else return QJsonArray(d, static_cast(d->header->root())) == QJsonArray(other.d, static_cast(other.d->header->root())); } /*! returns true if \a other is not equal to this document */ bool QJsonDocument::operator!=(const QJsonDocument &other) const { return !(*this == other); } /*! returns true if this document is valid. Documents created from utf-8 encoded text are validated during parsing. Documents created from binary data are however not validated, and an explicit call to isValid is required to perform the validation. This allow for optimised access to documents that are stored in binary format and originate from trusted sources. */ bool QJsonDocument::isValid() { if (!d) return false; if (d->valid == Private::Data::Unchecked) // Unchecked, check for validity d->validate(); if (d->valid != Private::Data::Validated) { if (!d->ref.deref()) delete d; d = 0; } return d != 0; } } // namespace QtJson QT_BEGIN_NAMESPACE QDebug operator<<(QDebug dbg, const QtJson::QJsonDocument &o) { if (!o.d) { dbg << "QJsonDocument()"; return dbg; } QByteArray json; if (o.d->header->root()->isArray()) QtJson::QJsonWriter::arrayToJson(static_cast(o.d->header->root()), json, 0, true); else QtJson::QJsonWriter::objectToJson(static_cast(o.d->header->root()), json, 0, true); dbg.nospace() << "QJsonDocument(" << json.constData() // print as utf-8 string without extra quotation marks << ")"; return dbg.space(); } QT_END_NAMESPACE