aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhjk <hjk@theqtcompany.com>2015-11-05 08:38:41 +0100
committerhjk <hjk@theqtcompany.com>2015-11-09 08:25:11 +0000
commitb8a8fe34a2a49ba6df23de98837c5ceb665f48c9 (patch)
tree91a66a79586d9a2115bcae886aaf282da3a84493
parent626086e9d1f67f44aab083bea077aa20ebddbbf1 (diff)
Introduce a Qt-free JSON implementation
This is essentially QJson with Qt replaced by std:: features. This is useful to have in circumstances where a Qt dependency is undesirable, e.g. for the Qt Creator debugger protocol implementation in our CDB extension. Change-Id: Iec79c6b23b1e717ce1b6f4d38755287d1f479c13 Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
-rw-r--r--src/shared/json/README.md3
-rw-r--r--src/shared/json/json.cpp4928
-rw-r--r--src/shared/json/json.h582
-rw-r--r--src/shared/json/json.pri2
-rw-r--r--src/shared/json/json.qbs15
-rw-r--r--src/src.qbs1
-rw-r--r--tests/auto/auto.pro1
-rw-r--r--tests/auto/auto.qbs1
-rw-r--r--tests/auto/json/bom.json3
-rw-r--r--tests/auto/json/json.pro13
-rw-r--r--tests/auto/json/json.qbs22
-rw-r--r--tests/auto/json/test.bjsonbin0 -> 39684 bytes
-rw-r--r--tests/auto/json/test.json66
-rw-r--r--tests/auto/json/test2.json1
-rw-r--r--tests/auto/json/test3.json15
-rw-r--r--tests/auto/json/tst_json.cpp2524
-rw-r--r--tests/benchmarks/json/json.pro14
-rw-r--r--tests/benchmarks/json/numbers.json19
-rw-r--r--tests/benchmarks/json/test.json66
-rw-r--r--tests/benchmarks/json/tst_bench_json.cpp269
20 files changed, 8545 insertions, 0 deletions
diff --git a/src/shared/json/README.md b/src/shared/json/README.md
new file mode 100644
index 00000000000..e641d8e5fe2
--- /dev/null
+++ b/src/shared/json/README.md
@@ -0,0 +1,3 @@
+This is QJson without Qt, to be used in circumstances
+where a Qt dependency is not desirable, such as
+qtcreatorcdbex.
diff --git a/src/shared/json/json.cpp b/src/shared/json/json.cpp
new file mode 100644
index 00000000000..6cb7502848a
--- /dev/null
+++ b/src/shared/json/json.cpp
@@ -0,0 +1,4928 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <algorithm>
+#include <atomic>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <limits.h>
+#include <string.h>
+
+#include "json.h"
+
+
+//#define PARSER_DEBUG
+#ifdef PARSER_DEBUG
+static int indent = 0;
+#define BEGIN std::cerr << std::string(4*indent++, ' ').data() << " pos=" << current
+#define END --indent
+#define DEBUG std::cerr << std::string(4*indent, ' ').data()
+#else
+#define BEGIN if (1) ; else std::cerr
+#define END do {} while (0)
+#define DEBUG if (1) ; else std::cerr
+#endif
+
+static const int nestingLimit = 1024;
+
+
+namespace Json {
+namespace Internal {
+
+/*
+ This defines a binary data structure for Json data. The data structure is optimised for fast reading
+ and minimum allocations. The whole data structure can be mmap'ed and used directly.
+
+ In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but
+ much faster to access.
+
+ The size requirements are:
+
+ String: 4 bytes header + 2*(string.length())
+
+ Values: 4 bytes + size of data (size can be 0 for some data)
+ bool: 0 bytes
+ double: 8 bytes (0 if integer with less than 27bits)
+ string: see above
+ array: size of array
+ object: size of object
+ Array: 12 bytes + 4*length + size of Value data
+ Object: 12 bytes + 8*length + size of Key Strings + size of Value data
+
+ For an example such as
+
+ { // object: 12 + 5*8 = 52
+ "firstName": "John", // key 12, value 8 = 20
+ "lastName" : "Smith", // key 12, value 8 = 20
+ "age" : 25, // key 8, value 0 = 8
+ "address" : // key 12, object below = 140
+ { // object: 12 + 4*8
+ "streetAddress": "21 2nd Street", // key 16, value 16
+ "city" : "New York", // key 8, value 12
+ "state" : "NY", // key 8, value 4
+ "postalCode" : "10021" // key 12, value 8
+ }, // object total: 128
+ "phoneNumber": // key: 16, value array below = 172
+ [ // array: 12 + 2*4 + values below: 156
+ { // object 12 + 2*8
+ "type" : "home", // key 8, value 8
+ "number": "212 555-1234" // key 8, value 16
+ }, // object total: 68
+ { // object 12 + 2*8
+ "type" : "fax", // key 8, value 8
+ "number": "646 555-4567" // key 8, value 16
+ } // object total: 68
+ ] // array total: 156
+ } // great total: 412 bytes
+
+ The uncompressed text file used roughly 500 bytes, so in this case we end up using about
+ the same space as the text representation.
+
+ Other measurements have shown a slightly bigger binary size than a compact text
+ representation where all possible whitespace was stripped out.
+*/
+
+class Array;
+class Object;
+class Value;
+class Entry;
+
+template<int pos, int width>
+class qle_bitfield
+{
+public:
+ uint32_t val;
+
+ enum {
+ mask = ((1u << width) - 1) << pos
+ };
+
+ void operator=(uint32_t t) {
+ uint32_t i = val;
+ i &= ~mask;
+ i |= t << pos;
+ val = i;
+ }
+ operator uint32_t() const {
+ uint32_t t = val;
+ t &= mask;
+ t >>= pos;
+ return t;
+ }
+ bool operator!() const { return !operator uint32_t(); }
+ bool operator==(uint32_t t) { return uint32_t(*this) == t; }
+ bool operator!=(uint32_t t) { return uint32_t(*this) != t; }
+ bool operator<(uint32_t t) { return uint32_t(*this) < t; }
+ bool operator>(uint32_t t) { return uint32_t(*this) > t; }
+ bool operator<=(uint32_t t) { return uint32_t(*this) <= t; }
+ bool operator>=(uint32_t t) { return uint32_t(*this) >= t; }
+ void operator+=(uint32_t i) { *this = (uint32_t(*this) + i); }
+ void operator-=(uint32_t i) { *this = (uint32_t(*this) - i); }
+};
+
+template<int pos, int width>
+class qle_signedbitfield
+{
+public:
+ uint32_t val;
+
+ enum {
+ mask = ((1u << width) - 1) << pos
+ };
+
+ void operator=(int t) {
+ uint32_t i = val;
+ i &= ~mask;
+ i |= t << pos;
+ val = i;
+ }
+ operator int() const {
+ uint32_t i = val;
+ i <<= 32 - width - pos;
+ int t = (int) i;
+ t >>= pos;
+ return t;
+ }
+
+ bool operator!() const { return !operator int(); }
+ bool operator==(int t) { return int(*this) == t; }
+ bool operator!=(int t) { return int(*this) != t; }
+ bool operator<(int t) { return int(*this) < t; }
+ bool operator>(int t) { return int(*this) > t; }
+ bool operator<=(int t) { return int(*this) <= t; }
+ bool operator>=(int t) { return int(*this) >= t; }
+ void operator+=(int i) { *this = (int(*this) + i); }
+ void operator-=(int i) { *this = (int(*this) - i); }
+};
+
+typedef uint32_t offset;
+
+// round the size up to the next 4 byte boundary
+int alignedSize(int size) { return (size + 3) & ~3; }
+
+static int qStringSize(const std::string &ba)
+{
+ int l = 4 + ba.length();
+ return alignedSize(l);
+}
+
+// returns INT_MAX if it can't compress it into 28 bits
+static int compressedNumber(double d)
+{
+ // this relies on details of how ieee floats are represented
+ const int exponent_off = 52;
+ const uint64_t fraction_mask = 0x000fffffffffffffull;
+ const uint64_t exponent_mask = 0x7ff0000000000000ull;
+
+ uint64_t val;
+ memcpy (&val, &d, sizeof(double));
+ int exp = (int)((val & exponent_mask) >> exponent_off) - 1023;
+ if (exp < 0 || exp > 25)
+ return INT_MAX;
+
+ uint64_t non_int = val & (fraction_mask >> exp);
+ if (non_int)
+ return INT_MAX;
+
+ bool neg = (val >> 63);
+ val &= fraction_mask;
+ val |= ((uint64_t)1 << 52);
+ int res = (int)(val >> (52 - exp));
+ return neg ? -res : res;
+}
+
+class String
+{
+public:
+ String(const char *data) { d = (Data *)data; }
+
+ struct Data {
+ int length;
+ char utf8[1];
+ };
+
+ Data *d;
+
+ void operator=(const std::string &ba)
+ {
+ d->length = ba.length();
+ memcpy(d->utf8, ba.data(), ba.length());
+ }
+
+ bool operator==(const std::string &ba) const {
+ return toString() == ba;
+ }
+ bool operator!=(const std::string &str) const {
+ return !operator==(str);
+ }
+ bool operator>=(const std::string &str) const {
+ // ###
+ return toString() >= str;
+ }
+
+ bool operator==(const String &str) const {
+ if (d->length != str.d->length)
+ return false;
+ return !memcmp(d->utf8, str.d->utf8, d->length);
+ }
+ bool operator<(const String &other) const;
+ bool operator>=(const String &other) const { return !(*this < other); }
+
+ std::string toString() const {
+ return std::string(d->utf8, d->length);
+ }
+
+};
+
+bool String::operator<(const String &other) const
+{
+ int alen = d->length;
+ int blen = other.d->length;
+ int l = std::min(alen, blen);
+ char *a = d->utf8;
+ char *b = other.d->utf8;
+
+ while (l-- && *a == *b)
+ a++,b++;
+ if (l==-1)
+ return (alen < blen);
+ return (unsigned char)(*a) < (unsigned char)(*b);
+}
+
+static void copyString(char *dest, const std::string &str)
+{
+ String string(dest);
+ string = str;
+}
+
+
+/*
+ Base is the base class for both Object and Array. Both classe work more or less the same way.
+ The class starts with a header (defined by the struct below), then followed by data (the data for
+ values in the Array case and Entry's (see below) for objects.
+
+ After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and
+ offsets from the beginning of the object to Entry's in the case of Object.
+
+ Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage
+ of a binary search over the keys in an Object.
+ */
+class Base
+{
+public:
+ uint32_t size;
+ union {
+ uint32_t _dummy;
+ qle_bitfield<0, 1> is_object;
+ qle_bitfield<1, 31> length;
+ };
+ offset tableOffset;
+ // content follows here
+
+ bool isObject() const { return is_object; }
+ bool isArray() const { return !isObject(); }
+
+ offset *table() const { return (offset *) (((char *) this) + tableOffset); }
+
+ int reserveSpace(uint32_t dataSize, int posInTable, uint32_t numItems, bool replace);
+ void removeItems(int pos, int numItems);
+};
+
+class Object : public Base
+{
+public:
+ Entry *entryAt(int i) const {
+ return reinterpret_cast<Entry *>(((char *)this) + table()[i]);
+ }
+ int indexOf(const std::string &key, bool *exists);
+
+ bool isValid() const;
+};
+
+
+class Value
+{
+public:
+ enum {
+ MaxSize = (1<<27) - 1
+ };
+ union {
+ uint32_t _dummy;
+ qle_bitfield<0, 3> type;
+ qle_bitfield<3, 1> intValue;
+ qle_bitfield<4, 1> _; // Ex-latin1Key
+ qle_bitfield<5, 27> value; // Used as offset in case of Entry(?)
+ qle_signedbitfield<5, 27> int_value;
+ };
+
+ char *data(const Base *b) const { return ((char *)b) + value; }
+ int usedStorage(const Base *b) const;
+
+ bool toBoolean() const { return value != 0; }
+ double toDouble(const Base *b) const;
+ std::string toString(const Base *b) const;
+ Base *base(const Base *b) const;
+
+ bool isValid(const Base *b) const;
+
+ static int requiredStorage(JsonValue &v, bool *compressed);
+ static uint32_t valueToStore(const JsonValue &v, uint32_t offset);
+ static void copyData(const JsonValue &v, char *dest, bool compressed);
+};
+
+class Array : public Base
+{
+public:
+ Value at(int i) const { return *(Value *) (table() + i); }
+ Value &operator[](int i) { return *(Value *) (table() + i); }
+
+ bool isValid() const;
+};
+
+class Entry {
+public:
+ Value value;
+ // key
+ // value data follows key
+
+ int size() const
+ {
+ int s = sizeof(Entry);
+ s += sizeof(uint32_t) + (*(int *) ((const char *)this + sizeof(Entry)));
+ return alignedSize(s);
+ }
+
+ int usedStorage(Base *b) const
+ {
+ return size() + value.usedStorage(b);
+ }
+
+ String shallowKey() const
+ {
+ return String((const char *)this + sizeof(Entry));
+ }
+
+ std::string key() const
+ {
+ return shallowKey().toString();
+ }
+
+ bool operator==(const std::string &key) const;
+ bool operator!=(const std::string &key) const { return !operator==(key); }
+ bool operator>=(const std::string &key) const { return shallowKey() >= key; }
+
+ bool operator==(const Entry &other) const;
+ bool operator>=(const Entry &other) const;
+};
+
+bool operator<(const std::string &key, const Entry &e)
+{
+ return e >= key;
+}
+
+
+class Header
+{
+public:
+ uint32_t tag; // 'qbjs'
+ uint32_t version; // 1
+ Base *root() { return (Base *)(this + 1); }
+};
+
+
+double Value::toDouble(const Base *b) const
+{
+ // assert(type == JsonValue::Double);
+ if (intValue)
+ return int_value;
+
+ double d;
+ memcpy(&d, (const char *)b + value, 8);
+ return d;
+}
+
+std::string Value::toString(const Base *b) const
+{
+ String s(data(b));
+ return s.toString();
+}
+
+Base *Value::base(const Base *b) const
+{
+ // assert(type == JsonValue::Array || type == JsonValue::Object);
+ return reinterpret_cast<Base *>(data(b));
+}
+
+class AtomicInt
+{
+public:
+ bool ref() { return ++x != 0; }
+ bool deref() { return --x != 0; }
+ int load() { return x.load(std::memory_order_seq_cst); }
+private:
+ std::atomic<int> x { 0 };
+};
+
+
+class SharedString
+{
+public:
+ AtomicInt ref;
+ std::string s;
+};
+
+class Data {
+public:
+ enum Validation {
+ Unchecked,
+ Validated,
+ Invalid
+ };
+
+ AtomicInt ref;
+ int alloc;
+ union {
+ char *rawData;
+ Header *header;
+ };
+ uint32_t compactionCounter : 31;
+ uint32_t ownsData : 1;
+
+ Data(char *raw, int a)
+ : alloc(a), rawData(raw), compactionCounter(0), ownsData(true)
+ {
+ }
+ Data(int reserved, JsonValue::Type valueType)
+ : rawData(0), compactionCounter(0), ownsData(true)
+ {
+ // assert(valueType == JsonValue::Array || valueType == JsonValue::Object);
+
+ alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset);
+ header = (Header *)malloc(alloc);
+ header->tag = JsonDocument::BinaryFormatTag;
+ header->version = 1;
+ Base *b = header->root();
+ b->size = sizeof(Base);
+ b->is_object = (valueType == JsonValue::Object);
+ b->tableOffset = sizeof(Base);
+ b->length = 0;
+ }
+ ~Data()
+ { if (ownsData) free(rawData); }
+
+ uint32_t offsetOf(const void *ptr) const { return (uint32_t)(((char *)ptr - rawData)); }
+
+ JsonObject toObject(Object *o) const
+ {
+ return JsonObject(const_cast<Data *>(this), o);
+ }
+
+ JsonArray toArray(Array *a) const
+ {
+ return JsonArray(const_cast<Data *>(this), a);
+ }
+
+ Data *clone(Base *b, int reserve = 0)
+ {
+ int size = sizeof(Header) + b->size;
+ if (b == header->root() && ref.load() == 1 && alloc >= size + reserve)
+ return this;
+
+ if (reserve) {
+ if (reserve < 128)
+ reserve = 128;
+ size = std::max(size + reserve, size *2);
+ }
+ char *raw = (char *)malloc(size);
+ memcpy(raw + sizeof(Header), b, b->size);
+ Header *h = (Header *)raw;
+ h->tag = JsonDocument::BinaryFormatTag;
+ h->version = 1;
+ Data *d = new Data(raw, size);
+ d->compactionCounter = (b == header->root()) ? compactionCounter : 0;
+ return d;
+ }
+
+ void compact();
+ bool valid() const;
+
+private:
+ Data(const Data &);
+ void operator=(const Data &);
+};
+
+
+void objectToJson(const Object *o, std::string &json, int indent, bool compact = false);
+void arrayToJson(const Array *a, std::string &json, int indent, bool compact = false);
+
+class Parser
+{
+public:
+ Parser(const char *json, int length);
+
+ JsonDocument parse(JsonParseError *error);
+
+ class ParsedObject
+ {
+ public:
+ ParsedObject(Parser *p, int pos) : parser(p), objectPosition(pos) {
+ offsets.reserve(64);
+ }
+ void insert(uint32_t offset);
+
+ Parser *parser;
+ int objectPosition;
+ std::vector<uint32_t> offsets;
+
+ Entry *entryAt(int i) const {
+ return reinterpret_cast<Entry *>(parser->data + objectPosition + offsets[i]);
+ }
+ };
+
+
+private:
+ void eatBOM();
+ bool eatSpace();
+ char nextToken();
+
+ bool parseObject();
+ bool parseArray();
+ bool parseMember(int baseOffset);
+ bool parseString();
+ bool parseValue(Value *val, int baseOffset);
+ bool parseNumber(Value *val, int baseOffset);
+ const char *head;
+ const char *json;
+ const char *end;
+
+ char *data;
+ int dataLength;
+ int current;
+ int nestingLevel;
+ JsonParseError::ParseError lastError;
+
+ int reserveSpace(int space) {
+ if (current + space >= dataLength) {
+ dataLength = 2*dataLength + space;
+ data = (char *)realloc(data, dataLength);
+ }
+ int pos = current;
+ current += space;
+ return pos;
+ }
+};
+
+} // namespace Internal
+
+using namespace Internal;
+
+/*!
+ \class JsonValue
+ \inmodule QtCore
+ \ingroup json
+ \ingroup shared
+ \reentrant
+ \since 5.0
+
+ \brief The JsonValue class encapsulates a value in JSON.
+
+ 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:
+
+ \list
+ \li bool JsonValue::Bool
+ \li double JsonValue::Double
+ \li string JsonValue::String
+ \li array JsonValue::Array
+ \li object JsonValue::Object
+ \li null JsonValue::Null
+ \endlist
+
+ A value can represent any of the above data types. In addition, JsonValue has one special
+ flag to represent undefined values. This can be queried with isUndefined().
+
+ The type of the value can be queried with type() or accessors like isBool(), isString(), and so on.
+ Likewise, the value can be converted to the type stored in it using the toBool(), toString() and so on.
+
+ Values are strictly typed internally and contrary to QVariant will not attempt to do any implicit type
+ conversions. This implies that converting to a type that is not stored in the value will return a default
+ constructed return value.
+
+ \section1 JsonValueRef
+
+ JsonValueRef is a helper class for JsonArray and JsonObject.
+ When you get an object of type JsonValueRef, you can
+ use it as if it were a reference to a JsonValue. If you assign to it,
+ the assignment will apply to the element in the JsonArray or JsonObject
+ from which you got the reference.
+
+ The following methods return JsonValueRef:
+ \list
+ \li \l {JsonArray}::operator[](int i)
+ \li \l {JsonObject}::operator[](const QString & key) const
+ \endlist
+
+ \sa {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*!
+ Creates a JsonValue of type \a type.
+
+ The default is to create a Null value.
+ */
+JsonValue::JsonValue(Type type)
+ : ui(0), d(0), t(type)
+{
+}
+
+/*!
+ \internal
+ */
+JsonValue::JsonValue(Internal::Data *data, Internal::Base *base, const Internal::Value &v)
+ : d(0), t((Type)(uint32_t)v.type)
+{
+ switch (t) {
+ case Undefined:
+ case Null:
+ dbl = 0;
+ break;
+ case Bool:
+ b = v.toBoolean();
+ break;
+ case Double:
+ dbl = v.toDouble(base);
+ break;
+ case String: {
+ stringData = new Internal::SharedString;
+ stringData->s = v.toString(base);
+ stringData->ref.ref();
+ break;
+ }
+ case Array:
+ case Object:
+ d = data;
+ this->base = v.base(base);
+ break;
+ }
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Creates a value of type Bool, with value \a b.
+ */
+JsonValue::JsonValue(bool b)
+ : d(0), t(Bool)
+{
+ this->b = b;
+}
+
+/*!
+ Creates a value of type Double, with value \a n.
+ */
+JsonValue::JsonValue(double n)
+ : d(0), t(Double)
+{
+ this->dbl = n;
+}
+
+/*!
+ \overload
+ Creates a value of type Double, with value \a n.
+ */
+JsonValue::JsonValue(int n)
+ : d(0), t(Double)
+{
+ this->dbl = n;
+}
+
+/*!
+ \overload
+ Creates a value of type Double, with value \a n.
+ NOTE: the integer limits for IEEE 754 double precision data is 2^53 (-9007199254740992 to +9007199254740992).
+ If you pass in values outside this range expect a loss of precision to occur.
+ */
+JsonValue::JsonValue(int64_t n)
+ : d(0), t(Double)
+{
+ this->dbl = n;
+}
+
+/*!
+ Creates a value of type String, with value \a s.
+ */
+JsonValue::JsonValue(const std::string &s)
+ : d(0), t(String)
+{
+ stringData = new Internal::SharedString;
+ stringData->s = s;
+ stringData->ref.ref();
+}
+
+JsonValue::JsonValue(const char *s)
+ : d(0), t(String)
+{
+ stringData = new Internal::SharedString;
+ stringData->s = s;
+ stringData->ref.ref();
+}
+
+/*!
+ Creates a value of type Array, with value \a a.
+ */
+JsonValue::JsonValue(const JsonArray &a)
+ : d(a.d), t(Array)
+{
+ base = a.a;
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Creates a value of type Object, with value \a o.
+ */
+JsonValue::JsonValue(const JsonObject &o)
+ : d(o.d), t(Object)
+{
+ base = o.o;
+ if (d)
+ d->ref.ref();
+}
+
+
+/*!
+ Destroys the value.
+ */
+JsonValue::~JsonValue()
+{
+ if (t == String && stringData && !stringData->ref.deref())
+ free(stringData);
+
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Creates a copy of \a other.
+ */
+JsonValue::JsonValue(const JsonValue &other)
+ : t(other.t)
+{
+ d = other.d;
+ ui = other.ui;
+ if (d)
+ d->ref.ref();
+
+ if (t == String && stringData)
+ stringData->ref.ref();
+}
+
+/*!
+ Assigns the value stored in \a other to this object.
+ */
+JsonValue &JsonValue::operator=(const JsonValue &other)
+{
+ if (t == String && stringData && !stringData->ref.deref())
+ free(stringData);
+
+ t = other.t;
+ dbl = other.dbl;
+
+ if (d != other.d) {
+
+ if (d && !d->ref.deref())
+ delete d;
+ d = other.d;
+ if (d)
+ d->ref.ref();
+
+ }
+
+ if (t == String && stringData)
+ stringData->ref.ref();
+
+ return *this;
+}
+
+/*!
+ \fn bool JsonValue::isNull() const
+
+ Returns \c true if the value is null.
+*/
+
+/*!
+ \fn bool JsonValue::isBool() const
+
+ Returns \c true if the value contains a boolean.
+
+ \sa toBool()
+ */
+
+/*!
+ \fn bool JsonValue::isDouble() const
+
+ Returns \c true if the value contains a double.
+
+ \sa toDouble()
+ */
+
+/*!
+ \fn bool JsonValue::isString() const
+
+ Returns \c true if the value contains a string.
+
+ \sa toString()
+ */
+
+/*!
+ \fn bool JsonValue::isArray() const
+
+ Returns \c true if the value contains an array.
+
+ \sa toArray()
+ */
+
+/*!
+ \fn bool JsonValue::isObject() const
+
+ Returns \c true if the value contains an object.
+
+ \sa toObject()
+ */
+
+/*!
+ \fn bool JsonValue::isUndefined() const
+
+ Returns \c true if the value is undefined. This can happen in certain
+ error cases as e.g. accessing a non existing key in a JsonObject.
+ */
+
+
+/*!
+ \enum JsonValue::Type
+
+ This enum describes the type of the JSON value.
+
+ \value Null A Null value
+ \value Bool A boolean value. Use toBool() to convert to a bool.
+ \value Double A double. Use toDouble() to convert to a double.
+ \value String A string. Use toString() to convert to a QString.
+ \value Array An array. Use toArray() to convert to a JsonArray.
+ \value Object An object. Use toObject() to convert to a JsonObject.
+ \value Undefined The value is undefined. This is usually returned as an
+ error condition, when trying to read an out of bounds value
+ in an array or a non existent key in an object.
+*/
+
+/*!
+ Returns the type of the value.
+
+ \sa JsonValue::Type
+ */
+
+
+/*!
+ Converts the value to a bool and returns it.
+
+ If type() is not bool, the \a defaultValue will be returned.
+ */
+bool JsonValue::toBool(bool defaultValue) const
+{
+ if (t != Bool)
+ return defaultValue;
+ return b;
+}
+
+/*!
+ Converts the value to an int and returns it.
+
+ If type() is not Double or the value is not a whole number,
+ the \a defaultValue will be returned.
+ */
+int JsonValue::toInt(int defaultValue) const
+{
+ if (t == Double && int(dbl) == dbl)
+ return dbl;
+ return defaultValue;
+}
+
+/*!
+ Converts the value to a double and returns it.
+
+ If type() is not Double, the \a defaultValue will be returned.
+ */
+double JsonValue::toDouble(double defaultValue) const
+{
+ if (t != Double)
+ return defaultValue;
+ return dbl;
+}
+
+/*!
+ Converts the value to a QString and returns it.
+
+ If type() is not String, the \a defaultValue will be returned.
+ */
+std::string JsonValue::toString(const std::string &defaultValue) const
+{
+ if (t != String)
+ return defaultValue;
+ return stringData->s;
+}
+
+/*!
+ Converts the value to an array and returns it.
+
+ If type() is not Array, the \a defaultValue will be returned.
+ */
+JsonArray JsonValue::toArray(const JsonArray &defaultValue) const
+{
+ if (!d || t != Array)
+ return defaultValue;
+
+ return JsonArray(d, static_cast<Internal::Array *>(base));
+}
+
+/*!
+ \overload
+
+ Converts the value to an array and returns it.
+
+ If type() is not Array, a \l{JsonArray::}{JsonArray()} will be returned.
+ */
+JsonArray JsonValue::toArray() const
+{
+ return toArray(JsonArray());
+}
+
+/*!
+ Converts the value to an object and returns it.
+
+ If type() is not Object, the \a defaultValue will be returned.
+ */
+JsonObject JsonValue::toObject(const JsonObject &defaultValue) const
+{
+ if (!d || t != Object)
+ return defaultValue;
+
+ return JsonObject(d, static_cast<Internal::Object *>(base));
+}
+
+/*!
+ \overload
+
+ Converts the value to an object and returns it.
+
+ If type() is not Object, the \l {JsonObject::}{JsonObject()} will be returned.
+*/
+JsonObject JsonValue::toObject() const
+{
+ return toObject(JsonObject());
+}
+
+/*!
+ Returns \c true if the value is equal to \a other.
+ */
+bool JsonValue::operator==(const JsonValue &other) const
+{
+ if (t != other.t)
+ return false;
+
+ switch (t) {
+ case Undefined:
+ case Null:
+ break;
+ case Bool:
+ return b == other.b;
+ case Double:
+ return dbl == other.dbl;
+ case String:
+ return toString() == other.toString();
+ case Array:
+ if (base == other.base)
+ return true;
+ if (!base)
+ return !other.base->length;
+ if (!other.base)
+ return !base->length;
+ return JsonArray(d, static_cast<Internal::Array *>(base))
+ == JsonArray(other.d, static_cast<Internal::Array *>(other.base));
+ case Object:
+ if (base == other.base)
+ return true;
+ if (!base)
+ return !other.base->length;
+ if (!other.base)
+ return !base->length;
+ return JsonObject(d, static_cast<Internal::Object *>(base))
+ == JsonObject(other.d, static_cast<Internal::Object *>(other.base));
+ }
+ return true;
+}
+
+/*!
+ Returns \c true if the value is not equal to \a other.
+ */
+bool JsonValue::operator!=(const JsonValue &other) const
+{
+ return !(*this == other);
+}
+
+/*!
+ \internal
+ */
+void JsonValue::detach()
+{
+ if (!d)
+ return;
+
+ Internal::Data *x = d->clone(base);
+ x->ref.ref();
+ if (!d->ref.deref())
+ delete d;
+ d = x;
+ base = static_cast<Internal::Object *>(d->header->root());
+}
+
+
+/*!
+ \class JsonValueRef
+ \inmodule QtCore
+ \reentrant
+ \brief The JsonValueRef class is a helper class for JsonValue.
+
+ \internal
+
+ \ingroup json
+
+ When you get an object of type JsonValueRef, if you can assign to it,
+ the assignment will apply to the character in the string from
+ which you got the reference. That is its whole purpose in life.
+
+ You can use it exactly in the same way as a reference to a JsonValue.
+
+ The JsonValueRef becomes invalid once modifications are made to the
+ string: if you want to keep the character, copy it into a JsonValue.
+
+ Most of the JsonValue member functions also exist in JsonValueRef.
+ However, they are not explicitly documented here.
+*/
+
+
+JsonValueRef &JsonValueRef::operator=(const JsonValue &val)
+{
+ if (is_object)
+ o->setValueAt(index, val);
+ else
+ a->replace(index, val);
+
+ return *this;
+}
+
+JsonValueRef &JsonValueRef::operator=(const JsonValueRef &ref)
+{
+ if (is_object)
+ o->setValueAt(index, ref);
+ else
+ a->replace(index, ref);
+
+ return *this;
+}
+
+JsonArray JsonValueRef::toArray() const
+{
+ return toValue().toArray();
+}
+
+JsonObject JsonValueRef::toObject() const
+{
+ return toValue().toObject();
+}
+
+JsonValue JsonValueRef::toValue() const
+{
+ if (!is_object)
+ return a->at(index);
+ return o->valueAt(index);
+}
+
+/*!
+ \class JsonArray
+ \inmodule QtCore
+ \ingroup json
+ \ingroup shared
+ \reentrant
+ \since 5.0
+
+ \brief The JsonArray class encapsulates a JSON array.
+
+ A JSON array is a list of values. The list can be manipulated by inserting and
+ removing JsonValue's from the array.
+
+ A JsonArray can be converted to and from a QVariantList. You can query the
+ number of entries with size(), insert(), and removeAt() entries from it
+ and iterate over its content using the standard C++ iterator pattern.
+
+ JsonArray is an implicitly shared class and shares the data with the document
+ it has been created from as long as it is not being modified.
+
+ You can convert the array to and from text based JSON through JsonDocument.
+
+ \sa {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*!
+ \typedef JsonArray::Iterator
+
+ Qt-style synonym for JsonArray::iterator.
+*/
+
+/*!
+ \typedef JsonArray::ConstIterator
+
+ Qt-style synonym for JsonArray::const_iterator.
+*/
+
+/*!
+ \typedef JsonArray::size_type
+
+ Typedef for int. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonArray::value_type
+
+ Typedef for JsonValue. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonArray::difference_type
+
+ Typedef for int. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonArray::pointer
+
+ Typedef for JsonValue *. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonArray::const_pointer
+
+ Typedef for const JsonValue *. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonArray::reference
+
+ Typedef for JsonValue &. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonArray::const_reference
+
+ Typedef for const JsonValue &. Provided for STL compatibility.
+*/
+
+/*!
+ Creates an empty array.
+ */
+JsonArray::JsonArray()
+ : d(0), a(0)
+{
+}
+
+JsonArray::JsonArray(std::initializer_list<JsonValue> args)
+ : d(0), a(0)
+{
+ for (auto i = args.begin(); i != args.end(); ++i)
+ append(*i);
+}
+
+/*!
+ \fn JsonArray::JsonArray(std::initializer_list<JsonValue> args)
+ \since 5.4
+ Creates an array initialized from \a args initialization list.
+
+ JsonArray can be constructed in a way similar to JSON notation,
+ for example:
+ \code
+ JsonArray array = { 1, 2.2, QString() };
+ \endcode
+ */
+
+/*!
+ \internal
+ */
+JsonArray::JsonArray(Internal::Data *data, Internal::Array *array)
+ : d(data), a(array)
+{
+ // assert(data);
+ // assert(array);
+ d->ref.ref();
+}
+
+/*!
+ Deletes the array.
+ */
+JsonArray::~JsonArray()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Creates a copy of \a other.
+
+ Since JsonArray is implicitly shared, the copy is shallow
+ as long as the object doesn't get modified.
+ */
+JsonArray::JsonArray(const JsonArray &other)
+{
+ d = other.d;
+ a = other.a;
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Assigns \a other to this array.
+ */
+JsonArray &JsonArray::operator=(const JsonArray &other)
+{
+ if (d != other.d) {
+ if (d && !d->ref.deref())
+ delete d;
+ d = other.d;
+ if (d)
+ d->ref.ref();
+ }
+ a = other.a;
+
+ return *this;
+}
+
+/*! \fn JsonArray &JsonArray::operator+=(const JsonValue &value)
+
+ Appends \a value to the array, and returns a reference to the array itself.
+
+ \since 5.3
+ \sa append(), operator<<()
+*/
+
+/*! \fn JsonArray JsonArray::operator+(const JsonValue &value) const
+
+ Returns an array that contains all the items in this array followed
+ by the provided \a value.
+
+ \since 5.3
+ \sa operator+=()
+*/
+
+/*! \fn JsonArray &JsonArray::operator<<(const JsonValue &value)
+
+ Appends \a value to the array, and returns a reference to the array itself.
+
+ \since 5.3
+ \sa operator+=(), append()
+*/
+
+/*!
+ Returns the number of values stored in the array.
+ */
+int JsonArray::size() const
+{
+ if (!d)
+ return 0;
+
+ return (int)a->length;
+}
+
+/*!
+ \fn JsonArray::count() const
+
+ Same as size().
+
+ \sa size()
+*/
+
+/*!
+ Returns \c true if the object is empty. This is the same as size() == 0.
+
+ \sa size()
+ */
+bool JsonArray::isEmpty() const
+{
+ if (!d)
+ return true;
+
+ return !a->length;
+}
+
+/*!
+ Returns a JsonValue representing the value for index \a i.
+
+ The returned JsonValue is \c Undefined, if \a i is out of bounds.
+
+ */
+JsonValue JsonArray::at(int i) const
+{
+ if (!a || i < 0 || i >= (int)a->length)
+ return JsonValue(JsonValue::Undefined);
+
+ return JsonValue(d, a, a->at(i));
+}
+
+/*!
+ Returns the first value stored in the array.
+
+ Same as \c at(0).
+
+ \sa at()
+ */
+JsonValue JsonArray::first() const
+{
+ return at(0);
+}
+
+/*!
+ Returns the last value stored in the array.
+
+ Same as \c{at(size() - 1)}.
+
+ \sa at()
+ */
+JsonValue JsonArray::last() const
+{
+ return at(a ? (a->length - 1) : 0);
+}
+
+/*!
+ Inserts \a value at the beginning of the array.
+
+ This is the same as \c{insert(0, value)} and will prepend \a value to the array.
+
+ \sa append(), insert()
+ */
+void JsonArray::prepend(const JsonValue &value)
+{
+ insert(0, value);
+}
+
+/*!
+ Inserts \a value at the end of the array.
+
+ \sa prepend(), insert()
+ */
+void JsonArray::append(const JsonValue &value)
+{
+ insert(a ? (int)a->length : 0, value);
+}
+
+/*!
+ Removes the value at index position \a i. \a i must be a valid
+ index position in the array (i.e., \c{0 <= i < size()}).
+
+ \sa insert(), replace()
+ */
+void JsonArray::removeAt(int i)
+{
+ if (!a || i < 0 || i >= (int)a->length)
+ return;
+
+ detach();
+ a->removeItems(i, 1);
+ ++d->compactionCounter;
+ if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u)
+ compact();
+}
+
+/*! \fn void JsonArray::removeFirst()
+
+ Removes the first item in the array. Calling this function is
+ equivalent to calling \c{removeAt(0)}. The array must not be empty. If
+ the array can be empty, call isEmpty() before calling this
+ function.
+
+ \sa removeAt(), removeLast()
+*/
+
+/*! \fn void JsonArray::removeLast()
+
+ Removes the last item in the array. Calling this function is
+ equivalent to calling \c{removeAt(size() - 1)}. The array must not be
+ empty. If the array can be empty, call isEmpty() before calling
+ this function.
+
+ \sa removeAt(), removeFirst()
+*/
+
+/*!
+ Removes the item at index position \a i and returns it. \a i must
+ be a valid index position in the array (i.e., \c{0 <= i < size()}).
+
+ If you don't use the return value, removeAt() is more efficient.
+
+ \sa removeAt()
+ */
+JsonValue JsonArray::takeAt(int i)
+{
+ if (!a || i < 0 || i >= (int)a->length)
+ return JsonValue(JsonValue::Undefined);
+
+ JsonValue v(d, a, a->at(i));
+ removeAt(i); // detaches
+ return v;
+}
+
+/*!
+ Inserts \a value at index position \a i in the array. If \a i
+ is \c 0, the value is prepended to the array. If \a i is size(), the
+ value is appended to the array.
+
+ \sa append(), prepend(), replace(), removeAt()
+ */
+void JsonArray::insert(int i, const JsonValue &value)
+{
+ // assert (i >= 0 && i <= (a ? (int)a->length : 0));
+ JsonValue val = value;
+
+ bool compressed;
+ int valueSize = Internal::Value::requiredStorage(val, &compressed);
+
+ detach(valueSize + sizeof(Internal::Value));
+
+ if (!a->length)
+ a->tableOffset = sizeof(Internal::Array);
+
+ int valueOffset = a->reserveSpace(valueSize, i, 1, false);
+ if (!valueOffset)
+ return;
+
+ Internal::Value &v = (*a)[i];
+ v.type = (val.t == JsonValue::Undefined ? JsonValue::Null : val.t);
+ v.intValue = compressed;
+ v.value = Internal::Value::valueToStore(val, valueOffset);
+ if (valueSize)
+ Internal::Value::copyData(val, (char *)a + valueOffset, compressed);
+}
+
+/*!
+ \fn JsonArray::iterator JsonArray::insert(iterator before, const JsonValue &value)
+
+ Inserts \a value before the position pointed to by \a before, and returns an iterator
+ pointing to the newly inserted item.
+
+ \sa erase(), insert()
+*/
+
+/*!
+ \fn JsonArray::iterator JsonArray::erase(iterator it)
+
+ Removes the item pointed to by \a it, and returns an iterator pointing to the
+ next item.
+
+ \sa removeAt()
+*/
+
+/*!
+ Replaces the item at index position \a i with \a value. \a i must
+ be a valid index position in the array (i.e., \c{0 <= i < size()}).
+
+ \sa operator[](), removeAt()
+ */
+void JsonArray::replace(int i, const JsonValue &value)
+{
+ // assert (a && i >= 0 && i < (int)(a->length));
+ JsonValue val = value;
+
+ bool compressed;
+ int valueSize = Internal::Value::requiredStorage(val, &compressed);
+
+ detach(valueSize);
+
+ if (!a->length)
+ a->tableOffset = sizeof(Internal::Array);
+
+ int valueOffset = a->reserveSpace(valueSize, i, 1, true);
+ if (!valueOffset)
+ return;
+
+ Internal::Value &v = (*a)[i];
+ v.type = (val.t == JsonValue::Undefined ? JsonValue::Null : val.t);
+ v.intValue = compressed;
+ v.value = Internal::Value::valueToStore(val, valueOffset);
+ if (valueSize)
+ Internal::Value::copyData(val, (char *)a + valueOffset, compressed);
+
+ ++d->compactionCounter;
+ if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u)
+ compact();
+}
+
+/*!
+ Returns \c true if the array contains an occurrence of \a value, otherwise \c false.
+
+ \sa count()
+ */
+bool JsonArray::contains(const JsonValue &value) const
+{
+ for (int i = 0; i < size(); i++) {
+ if (at(i) == value)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the value at index position \a i as a modifiable reference.
+ \a i must be a valid index position in the array (i.e., \c{0 <= i <
+ size()}).
+
+ The return value is of type JsonValueRef, a helper class for JsonArray
+ and JsonObject. When you get an object of type JsonValueRef, you can
+ use it as if it were a reference to a JsonValue. If you assign to it,
+ the assignment will apply to the character in the JsonArray of JsonObject
+ from which you got the reference.
+
+ \sa at()
+ */
+JsonValueRef JsonArray::operator[](int i)
+{
+ // assert(a && i >= 0 && i < (int)a->length);
+ return JsonValueRef(this, i);
+}
+
+/*!
+ \overload
+
+ Same as at().
+ */
+JsonValue JsonArray::operator[](int i) const
+{
+ return at(i);
+}
+
+/*!
+ Returns \c true if this array is equal to \a other.
+ */
+bool JsonArray::operator==(const JsonArray &other) const
+{
+ if (a == other.a)
+ return true;
+
+ if (!a)
+ return !other.a->length;
+ if (!other.a)
+ return !a->length;
+ if (a->length != other.a->length)
+ return false;
+
+ for (int i = 0; i < (int)a->length; ++i) {
+ if (JsonValue(d, a, a->at(i)) != JsonValue(other.d, other.a, other.a->at(i)))
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns \c true if this array is not equal to \a other.
+ */
+bool JsonArray::operator!=(const JsonArray &other) const
+{
+ return !(*this == other);
+}
+
+/*! \fn JsonArray::iterator JsonArray::begin()
+
+ Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in
+ the array.
+
+ \sa constBegin(), end()
+*/
+
+/*! \fn JsonArray::const_iterator JsonArray::begin() const
+
+ \overload
+*/
+
+/*! \fn JsonArray::const_iterator JsonArray::constBegin() const
+
+ Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item
+ in the array.
+
+ \sa begin(), constEnd()
+*/
+
+/*! \fn JsonArray::iterator JsonArray::end()
+
+ Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item
+ after the last item in the array.
+
+ \sa begin(), constEnd()
+*/
+
+/*! \fn const_iterator JsonArray::end() const
+
+ \overload
+*/
+
+/*! \fn JsonArray::const_iterator JsonArray::constEnd() const
+
+ Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
+ item after the last item in the array.
+
+ \sa constBegin(), end()
+*/
+
+/*! \fn void JsonArray::push_back(const JsonValue &value)
+
+ This function is provided for STL compatibility. It is equivalent
+ to \l{JsonArray::append()}{append(value)} and will append \a value to the array.
+*/
+
+/*! \fn void JsonArray::push_front(const JsonValue &value)
+
+ This function is provided for STL compatibility. It is equivalent
+ to \l{JsonArray::prepend()}{prepend(value)} and will prepend \a value to the array.
+*/
+
+/*! \fn void JsonArray::pop_front()
+
+ This function is provided for STL compatibility. It is equivalent
+ to removeFirst(). The array must not be empty. If the array can be
+ empty, call isEmpty() before calling this function.
+*/
+
+/*! \fn void JsonArray::pop_back()
+
+ This function is provided for STL compatibility. It is equivalent
+ to removeLast(). The array must not be empty. If the array can be
+ empty, call isEmpty() before calling this function.
+*/
+
+/*! \fn bool JsonArray::empty() const
+
+ This function is provided for STL compatibility. It is equivalent
+ to isEmpty() and returns \c true if the array is empty.
+*/
+
+/*! \class JsonArray::iterator
+ \inmodule QtCore
+ \brief The JsonArray::iterator class provides an STL-style non-const iterator for JsonArray.
+
+ JsonArray::iterator allows you to iterate over a JsonArray
+ and to modify the array item associated with the
+ iterator. If you want to iterate over a const JsonArray, use
+ JsonArray::const_iterator instead. It is generally a good practice to
+ use JsonArray::const_iterator on a non-const JsonArray as well, unless
+ you need to change the JsonArray through the iterator. Const
+ iterators are slightly faster and improves code readability.
+
+ The default JsonArray::iterator constructor creates an uninitialized
+ iterator. You must initialize it using a JsonArray function like
+ JsonArray::begin(), JsonArray::end(), or JsonArray::insert() before you can
+ start iterating.
+
+ Most JsonArray functions accept an integer index rather than an
+ iterator. For that reason, iterators are rarely useful in
+ connection with JsonArray. One place where STL-style iterators do
+ make sense is as arguments to \l{generic algorithms}.
+
+ Multiple iterators can be used on the same array. However, be
+ aware that any non-const function call performed on the JsonArray
+ will render all existing iterators undefined.
+
+ \sa JsonArray::const_iterator
+*/
+
+/*! \typedef JsonArray::iterator::iterator_category
+
+ A synonym for \e {std::random_access_iterator_tag} indicating
+ this iterator is a random access iterator.
+*/
+
+/*! \typedef JsonArray::iterator::difference_type
+
+ \internal
+*/
+
+/*! \typedef JsonArray::iterator::value_type
+
+ \internal
+*/
+
+/*! \typedef JsonArray::iterator::reference
+
+ \internal
+*/
+
+/*! \typedef JsonArray::iterator::pointer
+
+ \internal
+*/
+
+/*! \fn JsonArray::iterator::iterator()
+
+ Constructs an uninitialized iterator.
+
+ Functions like operator*() and operator++() should not be called
+ on an uninitialized iterator. Use operator=() to assign a value
+ to it before using it.
+
+ \sa JsonArray::begin(), JsonArray::end()
+*/
+
+/*! \fn JsonArray::iterator::iterator(JsonArray *array, int index)
+ \internal
+*/
+
+/*! \fn JsonValueRef JsonArray::iterator::operator*() const
+
+
+ Returns a modifiable reference to the current item.
+
+ You can change the value of an item by using operator*() on the
+ left side of an assignment.
+
+ The return value is of type JsonValueRef, a helper class for JsonArray
+ and JsonObject. When you get an object of type JsonValueRef, you can
+ use it as if it were a reference to a JsonValue. If you assign to it,
+ the assignment will apply to the character in the JsonArray of JsonObject
+ from which you got the reference.
+*/
+
+/*! \fn JsonValueRef *JsonArray::iterator::operator->() const
+
+ Returns a pointer to a modifiable reference to the current item.
+*/
+
+/*! \fn JsonValueRef JsonArray::iterator::operator[](int j) const
+
+ Returns a modifiable reference to the item at offset \a j from the
+ item pointed to by this iterator (the item at position \c{*this + j}).
+
+ This function is provided to make JsonArray iterators behave like C++
+ pointers.
+
+ The return value is of type JsonValueRef, a helper class for JsonArray
+ and JsonObject. When you get an object of type JsonValueRef, you can
+ use it as if it were a reference to a JsonValue. If you assign to it,
+ the assignment will apply to the character in the JsonArray of JsonObject
+ from which you got the reference.
+
+ \sa operator+()
+*/
+
+/*!
+ \fn bool JsonArray::iterator::operator==(const iterator &other) const
+ \fn bool JsonArray::iterator::operator==(const const_iterator &other) const
+
+ Returns \c true if \a other points to the same item as this
+ iterator; otherwise returns \c false.
+
+ \sa operator!=()
+*/
+
+/*!
+ \fn bool JsonArray::iterator::operator!=(const iterator &other) const
+ \fn bool JsonArray::iterator::operator!=(const const_iterator &other) const
+
+ Returns \c true if \a other points to a different item than this
+ iterator; otherwise returns \c false.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool JsonArray::iterator::operator<(const iterator& other) const
+ \fn bool JsonArray::iterator::operator<(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool JsonArray::iterator::operator<=(const iterator& other) const
+ \fn bool JsonArray::iterator::operator<=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ or equal to the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool JsonArray::iterator::operator>(const iterator& other) const
+ \fn bool JsonArray::iterator::operator>(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool JsonArray::iterator::operator>=(const iterator& other) const
+ \fn bool JsonArray::iterator::operator>=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than or equal to the item pointed to by the \a other iterator.
+*/
+
+/*! \fn JsonArray::iterator &JsonArray::iterator::operator++()
+
+ The prefix ++ operator, \c{++it}, advances the iterator to the
+ next item in the array and returns an iterator to the new current
+ item.
+
+ Calling this function on JsonArray::end() leads to undefined results.
+
+ \sa operator--()
+*/
+
+/*! \fn JsonArray::iterator JsonArray::iterator::operator++(int)
+
+ \overload
+
+ The postfix ++ operator, \c{it++}, advances the iterator to the
+ next item in the array and returns an iterator to the previously
+ current item.
+*/
+
+/*! \fn JsonArray::iterator &JsonArray::iterator::operator--()
+
+ The prefix -- operator, \c{--it}, makes the preceding item
+ current and returns an iterator to the new current item.
+
+ Calling this function on JsonArray::begin() leads to undefined results.
+
+ \sa operator++()
+*/
+
+/*! \fn JsonArray::iterator JsonArray::iterator::operator--(int)
+
+ \overload
+
+ The postfix -- operator, \c{it--}, makes the preceding item
+ current and returns an iterator to the previously current item.
+*/
+
+/*! \fn JsonArray::iterator &JsonArray::iterator::operator+=(int j)
+
+ Advances the iterator by \a j items. If \a j is negative, the
+ iterator goes backward.
+
+ \sa operator-=(), operator+()
+*/
+
+/*! \fn JsonArray::iterator &JsonArray::iterator::operator-=(int j)
+
+ Makes the iterator go back by \a j items. If \a j is negative,
+ the iterator goes forward.
+
+ \sa operator+=(), operator-()
+*/
+
+/*! \fn JsonArray::iterator JsonArray::iterator::operator+(int j) const
+
+ Returns an iterator to the item at \a j positions forward from
+ this iterator. If \a j is negative, the iterator goes backward.
+
+ \sa operator-(), operator+=()
+*/
+
+/*! \fn JsonArray::iterator JsonArray::iterator::operator-(int j) const
+
+ Returns an iterator to the item at \a j positions backward from
+ this iterator. If \a j is negative, the iterator goes forward.
+
+ \sa operator+(), operator-=()
+*/
+
+/*! \fn int JsonArray::iterator::operator-(iterator other) const
+
+ Returns the number of items between the item pointed to by \a
+ other and the item pointed to by this iterator.
+*/
+
+/*! \class JsonArray::const_iterator
+ \inmodule QtCore
+ \brief The JsonArray::const_iterator class provides an STL-style const iterator for JsonArray.
+
+ JsonArray::const_iterator allows you to iterate over a
+ JsonArray. If you want to modify the JsonArray as
+ you iterate over it, use JsonArray::iterator instead. It is generally a
+ good practice to use JsonArray::const_iterator on a non-const JsonArray
+ as well, unless you need to change the JsonArray through the
+ iterator. Const iterators are slightly faster and improves
+ code readability.
+
+ The default JsonArray::const_iterator constructor creates an
+ uninitialized iterator. You must initialize it using a JsonArray
+ function like JsonArray::constBegin(), JsonArray::constEnd(), or
+ JsonArray::insert() before you can start iterating.
+
+ Most JsonArray functions accept an integer index rather than an
+ iterator. For that reason, iterators are rarely useful in
+ connection with JsonArray. One place where STL-style iterators do
+ make sense is as arguments to \l{generic algorithms}.
+
+ Multiple iterators can be used on the same array. However, be
+ aware that any non-const function call performed on the JsonArray
+ will render all existing iterators undefined.
+
+ \sa JsonArray::iterator
+*/
+
+/*! \fn JsonArray::const_iterator::const_iterator()
+
+ Constructs an uninitialized iterator.
+
+ Functions like operator*() and operator++() should not be called
+ on an uninitialized iterator. Use operator=() to assign a value
+ to it before using it.
+
+ \sa JsonArray::constBegin(), JsonArray::constEnd()
+*/
+
+/*! \fn JsonArray::const_iterator::const_iterator(const JsonArray *array, int index)
+ \internal
+*/
+
+/*! \typedef JsonArray::const_iterator::iterator_category
+
+ A synonym for \e {std::random_access_iterator_tag} indicating
+ this iterator is a random access iterator.
+*/
+
+/*! \typedef JsonArray::const_iterator::difference_type
+
+ \internal
+*/
+
+/*! \typedef JsonArray::const_iterator::value_type
+
+ \internal
+*/
+
+/*! \typedef JsonArray::const_iterator::reference
+
+ \internal
+*/
+
+/*! \typedef JsonArray::const_iterator::pointer
+
+ \internal
+*/
+
+/*! \fn JsonArray::const_iterator::const_iterator(const const_iterator &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*! \fn JsonArray::const_iterator::const_iterator(const iterator &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*! \fn JsonValue JsonArray::const_iterator::operator*() const
+
+ Returns the current item.
+*/
+
+/*! \fn JsonValue *JsonArray::const_iterator::operator->() const
+
+ Returns a pointer to the current item.
+*/
+
+/*! \fn JsonValue JsonArray::const_iterator::operator[](int j) const
+
+ Returns the item at offset \a j from the item pointed to by this iterator (the item at
+ position \c{*this + j}).
+
+ This function is provided to make JsonArray iterators behave like C++
+ pointers.
+
+ \sa operator+()
+*/
+
+/*! \fn bool JsonArray::const_iterator::operator==(const const_iterator &other) const
+
+ Returns \c true if \a other points to the same item as this
+ iterator; otherwise returns \c false.
+
+ \sa operator!=()
+*/
+
+/*! \fn bool JsonArray::const_iterator::operator!=(const const_iterator &other) const
+
+ Returns \c true if \a other points to a different item than this
+ iterator; otherwise returns \c false.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool JsonArray::const_iterator::operator<(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool JsonArray::const_iterator::operator<=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ or equal to the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool JsonArray::const_iterator::operator>(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool JsonArray::const_iterator::operator>=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than or equal to the item pointed to by the \a other iterator.
+*/
+
+/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator++()
+
+ The prefix ++ operator, \c{++it}, advances the iterator to the
+ next item in the array and returns an iterator to the new current
+ item.
+
+ Calling this function on JsonArray::end() leads to undefined results.
+
+ \sa operator--()
+*/
+
+/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator++(int)
+
+ \overload
+
+ The postfix ++ operator, \c{it++}, advances the iterator to the
+ next item in the array and returns an iterator to the previously
+ current item.
+*/
+
+/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator--()
+
+ The prefix -- operator, \c{--it}, makes the preceding item
+ current and returns an iterator to the new current item.
+
+ Calling this function on JsonArray::begin() leads to undefined results.
+
+ \sa operator++()
+*/
+
+/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator--(int)
+
+ \overload
+
+ The postfix -- operator, \c{it--}, makes the preceding item
+ current and returns an iterator to the previously current item.
+*/
+
+/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator+=(int j)
+
+ Advances the iterator by \a j items. If \a j is negative, the
+ iterator goes backward.
+
+ \sa operator-=(), operator+()
+*/
+
+/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator-=(int j)
+
+ Makes the iterator go back by \a j items. If \a j is negative,
+ the iterator goes forward.
+
+ \sa operator+=(), operator-()
+*/
+
+/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator+(int j) const
+
+ Returns an iterator to the item at \a j positions forward from
+ this iterator. If \a j is negative, the iterator goes backward.
+
+ \sa operator-(), operator+=()
+*/
+
+/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator-(int j) const
+
+ Returns an iterator to the item at \a j positions backward from
+ this iterator. If \a j is negative, the iterator goes forward.
+
+ \sa operator+(), operator-=()
+*/
+
+/*! \fn int JsonArray::const_iterator::operator-(const_iterator other) const
+
+ Returns the number of items between the item pointed to by \a
+ other and the item pointed to by this iterator.
+*/
+
+
+/*!
+ \internal
+ */
+void JsonArray::detach(uint32_t reserve)
+{
+ if (!d) {
+ d = new Internal::Data(reserve, JsonValue::Array);
+ a = static_cast<Internal::Array *>(d->header->root());
+ d->ref.ref();
+ return;
+ }
+ if (reserve == 0 && d->ref.load() == 1)
+ return;
+
+ Internal::Data *x = d->clone(a, reserve);
+ x->ref.ref();
+ if (!d->ref.deref())
+ delete d;
+ d = x;
+ a = static_cast<Internal::Array *>(d->header->root());
+}
+
+/*!
+ \internal
+ */
+void JsonArray::compact()
+{
+ if (!d || !d->compactionCounter)
+ return;
+
+ detach();
+ d->compact();
+ a = static_cast<Internal::Array *>(d->header->root());
+}
+
+/*!
+ \class JsonObject
+ \inmodule QtCore
+ \ingroup json
+ \ingroup shared
+ \reentrant
+ \since 5.0
+
+ \brief The JsonObject class encapsulates a JSON object.
+
+ A JSON object is a list of key value pairs, where the keys are unique strings
+ and the values are represented by a JsonValue.
+
+ A JsonObject can be converted to and from a QVariantMap. You can query the
+ number of (key, value) pairs with size(), insert(), and remove() entries from it
+ and iterate over its content using the standard C++ iterator pattern.
+
+ JsonObject is an implicitly shared class, and shares the data with the document
+ it has been created from as long as it is not being modified.
+
+ You can convert the object to and from text based JSON through JsonDocument.
+
+ \sa {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*!
+ \typedef JsonObject::Iterator
+
+ Qt-style synonym for JsonObject::iterator.
+*/
+
+/*!
+ \typedef JsonObject::ConstIterator
+
+ Qt-style synonym for JsonObject::const_iterator.
+*/
+
+/*!
+ \typedef JsonObject::key_type
+
+ Typedef for QString. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonObject::mapped_type
+
+ Typedef for JsonValue. Provided for STL compatibility.
+*/
+
+/*!
+ \typedef JsonObject::size_type
+
+ Typedef for int. Provided for STL compatibility.
+*/
+
+
+/*!
+ Constructs an empty JSON object.
+
+ \sa isEmpty()
+ */
+JsonObject::JsonObject()
+ : d(0), o(0)
+{
+}
+
+JsonObject::JsonObject(std::initializer_list<std::pair<std::string, JsonValue> > args)
+ : d(0), o(0)
+{
+ for (auto i = args.begin(); i != args.end(); ++i)
+ insert(i->first, i->second);
+}
+
+/*!
+ \fn JsonObject::JsonObject(std::initializer_list<QPair<QString, JsonValue> > args)
+ \since 5.4
+ Constructs a JsonObject instance initialized from \a args initialization list.
+ For example:
+ \code
+ JsonObject object
+ {
+ {"property1", 1},
+ {"property2", 2}
+ };
+ \endcode
+*/
+
+/*!
+ \internal
+ */
+JsonObject::JsonObject(Internal::Data *data, Internal::Object *object)
+ : d(data), o(object)
+{
+ // assert(d);
+ // assert(o);
+ d->ref.ref();
+}
+
+/*!
+ This method replaces part of the JsonObject(std::initializer_list<QPair<QString, JsonValue>> args) body.
+ The constructor needs to be inline, but we do not want to leak implementation details
+ of this class.
+ \note this method is called for an uninitialized object
+ \internal
+ */
+
+/*!
+ Destroys the object.
+ */
+JsonObject::~JsonObject()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Creates a copy of \a other.
+
+ Since JsonObject is implicitly shared, the copy is shallow
+ as long as the object does not get modified.
+ */
+JsonObject::JsonObject(const JsonObject &other)
+{
+ d = other.d;
+ o = other.o;
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Assigns \a other to this object.
+ */
+JsonObject &JsonObject::operator=(const JsonObject &other)
+{
+ if (d != other.d) {
+ if (d && !d->ref.deref())
+ delete d;
+ d = other.d;
+ if (d)
+ d->ref.ref();
+ }
+ o = other.o;
+
+ return *this;
+}
+
+/*!
+ Returns a list of all keys in this object.
+
+ The list is sorted lexographically.
+ */
+JsonObject::Keys JsonObject::keys() const
+{
+ Keys keys;
+ if (!d)
+ return keys;
+
+ keys.reserve(o->length);
+ for (uint32_t i = 0; i < o->length; ++i) {
+ Internal::Entry *e = o->entryAt(i);
+ keys.push_back(e->key().data());
+ }
+
+ return keys;
+}
+
+/*!
+ Returns the number of (key, value) pairs stored in the object.
+ */
+int JsonObject::size() const
+{
+ if (!d)
+ return 0;
+
+ return o->length;
+}
+
+/*!
+ Returns \c true if the object is empty. This is the same as size() == 0.
+
+ \sa size()
+ */
+bool JsonObject::isEmpty() const
+{
+ if (!d)
+ return true;
+
+ return !o->length;
+}
+
+/*!
+ Returns a JsonValue representing the value for the key \a key.
+
+ The returned JsonValue is JsonValue::Undefined if the key does not exist.
+
+ \sa JsonValue, JsonValue::isUndefined()
+ */
+JsonValue JsonObject::value(const std::string &key) const
+{
+ if (!d)
+ return JsonValue(JsonValue::Undefined);
+
+ bool keyExists;
+ int i = o->indexOf(key, &keyExists);
+ if (!keyExists)
+ return JsonValue(JsonValue::Undefined);
+ return JsonValue(d, o, o->entryAt(i)->value);
+}
+
+/*!
+ Returns a JsonValue representing the value for the key \a key.
+
+ This does the same as value().
+
+ The returned JsonValue is JsonValue::Undefined if the key does not exist.
+
+ \sa value(), JsonValue, JsonValue::isUndefined()
+ */
+JsonValue JsonObject::operator[](const std::string &key) const
+{
+ return value(key);
+}
+
+/*!
+ Returns a reference to the value for \a key.
+
+ The return value is of type JsonValueRef, a helper class for JsonArray
+ and JsonObject. When you get an object of type JsonValueRef, you can
+ use it as if it were a reference to a JsonValue. If you assign to it,
+ the assignment will apply to the element in the JsonArray or JsonObject
+ from which you got the reference.
+
+ \sa value()
+ */
+JsonValueRef JsonObject::operator[](const std::string &key)
+{
+ // ### somewhat inefficient, as we lookup the key twice if it doesn't yet exist
+ bool keyExists = false;
+ int index = o ? o->indexOf(key, &keyExists) : -1;
+ if (!keyExists) {
+ iterator i = insert(key, JsonValue());
+ index = i.i;
+ }
+ return JsonValueRef(this, index);
+}
+
+/*!
+ Inserts a new item with the key \a key and a value of \a value.
+
+ If there is already an item with the key \a key, then that item's value
+ is replaced with \a value.
+
+ Returns an iterator pointing to the inserted item.
+
+ If the value is JsonValue::Undefined, it will cause the key to get removed
+ from the object. The returned iterator will then point to end().
+
+ \sa remove(), take(), JsonObject::iterator, end()
+ */
+JsonObject::iterator JsonObject::insert(const std::string &key, const JsonValue &value)
+{
+ if (value.t == JsonValue::Undefined) {
+ remove(key);
+ return end();
+ }
+ JsonValue val = value;
+
+ bool isIntValue;
+ int valueSize = Internal::Value::requiredStorage(val, &isIntValue);
+
+ int valueOffset = sizeof(Internal::Entry) + Internal::qStringSize(key);
+ int requiredSize = valueOffset + valueSize;
+
+ detach(requiredSize + sizeof(Internal::offset)); // offset for the new index entry
+
+ if (!o->length)
+ o->tableOffset = sizeof(Internal::Object);
+
+ bool keyExists = false;
+ int pos = o->indexOf(key, &keyExists);
+ if (keyExists)
+ ++d->compactionCounter;
+
+ uint32_t off = o->reserveSpace(requiredSize, pos, 1, keyExists);
+ if (!off)
+ return end();
+
+ Internal::Entry *e = o->entryAt(pos);
+ e->value.type = val.t;
+ e->value.intValue = isIntValue;
+ e->value.value = Internal::Value::valueToStore(val, (char *)e - (char *)o + valueOffset);
+ Internal::copyString((char *)(e + 1), key);
+ if (valueSize)
+ Internal::Value::copyData(val, (char *)e + valueOffset, isIntValue);
+
+ if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u)
+ compact();
+
+ return iterator(this, pos);
+}
+
+/*!
+ Removes \a key from the object.
+
+ \sa insert(), take()
+ */
+void JsonObject::remove(const std::string &key)
+{
+ if (!d)
+ return;
+
+ bool keyExists;
+ int index = o->indexOf(key, &keyExists);
+ if (!keyExists)
+ return;
+
+ detach();
+ o->removeItems(index, 1);
+ ++d->compactionCounter;
+ if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u)
+ compact();
+}
+
+/*!
+ Removes \a key from the object.
+
+ Returns a JsonValue containing the value referenced by \a key.
+ If \a key was not contained in the object, the returned JsonValue
+ is JsonValue::Undefined.
+
+ \sa insert(), remove(), JsonValue
+ */
+JsonValue JsonObject::take(const std::string &key)
+{
+ if (!o)
+ return JsonValue(JsonValue::Undefined);
+
+ bool keyExists;
+ int index = o->indexOf(key, &keyExists);
+ if (!keyExists)
+ return JsonValue(JsonValue::Undefined);
+
+ JsonValue v(d, o, o->entryAt(index)->value);
+ detach();
+ o->removeItems(index, 1);
+ ++d->compactionCounter;
+ if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u)
+ compact();
+
+ return v;
+}
+
+/*!
+ Returns \c true if the object contains key \a key.
+
+ \sa insert(), remove(), take()
+ */
+bool JsonObject::contains(const std::string &key) const
+{
+ if (!o)
+ return false;
+
+ bool keyExists;
+ o->indexOf(key, &keyExists);
+ return keyExists;
+}
+
+/*!
+ Returns \c true if \a other is equal to this object.
+ */
+bool JsonObject::operator==(const JsonObject &other) const
+{
+ if (o == other.o)
+ return true;
+
+ if (!o)
+ return !other.o->length;
+ if (!other.o)
+ return !o->length;
+ if (o->length != other.o->length)
+ return false;
+
+ for (uint32_t i = 0; i < o->length; ++i) {
+ Internal::Entry *e = o->entryAt(i);
+ JsonValue v(d, o, e->value);
+ if (other.value(e->key()) != v)
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ Returns \c true if \a other is not equal to this object.
+ */
+bool JsonObject::operator!=(const JsonObject &other) const
+{
+ return !(*this == other);
+}
+
+/*!
+ Removes the (key, value) pair pointed to by the iterator \a it
+ from the map, and returns an iterator to the next item in the
+ map.
+
+ \sa remove()
+ */
+JsonObject::iterator JsonObject::erase(JsonObject::iterator it)
+{
+ // assert(d && d->ref.load() == 1);
+ if (it.o != this || it.i < 0 || it.i >= (int)o->length)
+ return iterator(this, o->length);
+
+ int index = it.i;
+
+ o->removeItems(index, 1);
+ ++d->compactionCounter;
+ if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u)
+ compact();
+
+ // iterator hasn't changed
+ return it;
+}
+
+/*!
+ Returns an iterator pointing to the item with key \a key in the
+ map.
+
+ If the map contains no item with key \a key, the function
+ returns end().
+ */
+JsonObject::iterator JsonObject::find(const std::string &key)
+{
+ bool keyExists = false;
+ int index = o ? o->indexOf(key, &keyExists) : 0;
+ if (!keyExists)
+ return end();
+ detach();
+ return iterator(this, index);
+}
+
+/*! \fn JsonObject::const_iterator JsonObject::find(const QString &key) const
+
+ \overload
+*/
+
+/*!
+ Returns a const iterator pointing to the item with key \a key in the
+ map.
+
+ If the map contains no item with key \a key, the function
+ returns constEnd().
+ */
+JsonObject::const_iterator JsonObject::constFind(const std::string &key) const
+{
+ bool keyExists = false;
+ int index = o ? o->indexOf(key, &keyExists) : 0;
+ if (!keyExists)
+ return end();
+ return const_iterator(this, index);
+}
+
+/*! \fn int JsonObject::count() const
+
+ \overload
+
+ Same as size().
+*/
+
+/*! \fn int JsonObject::length() const
+
+ \overload
+
+ Same as size().
+*/
+
+/*! \fn JsonObject::iterator JsonObject::begin()
+
+ Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in
+ the object.
+
+ \sa constBegin(), end()
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::begin() const
+
+ \overload
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::constBegin() const
+
+ Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item
+ in the object.
+
+ \sa begin(), constEnd()
+*/
+
+/*! \fn JsonObject::iterator JsonObject::end()
+
+ Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item
+ after the last item in the object.
+
+ \sa begin(), constEnd()
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::end() const
+
+ \overload
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::constEnd() const
+
+ Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
+ item after the last item in the object.
+
+ \sa constBegin(), end()
+*/
+
+/*!
+ \fn bool JsonObject::empty() const
+
+ This function is provided for STL compatibility. It is equivalent
+ to isEmpty(), returning \c true if the object is empty; otherwise
+ returning \c false.
+*/
+
+/*! \class JsonObject::iterator
+ \inmodule QtCore
+ \ingroup json
+ \reentrant
+ \since 5.0
+
+ \brief The JsonObject::iterator class provides an STL-style non-const iterator for JsonObject.
+
+ JsonObject::iterator allows you to iterate over a JsonObject
+ and to modify the value (but not the key) stored under
+ a particular key. If you want to iterate over a const JsonObject, you
+ should use JsonObject::const_iterator. It is generally good practice to
+ use JsonObject::const_iterator on a non-const JsonObject as well, unless you
+ need to change the JsonObject through the iterator. Const iterators are
+ slightly faster, and improve code readability.
+
+ The default JsonObject::iterator constructor creates an uninitialized
+ iterator. You must initialize it using a JsonObject function like
+ JsonObject::begin(), JsonObject::end(), or JsonObject::find() before you can
+ start iterating.
+
+ Multiple iterators can be used on the same object. Existing iterators will however
+ become dangling once the object gets modified.
+
+ \sa JsonObject::const_iterator, {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*! \typedef JsonObject::iterator::difference_type
+
+ \internal
+*/
+
+/*! \typedef JsonObject::iterator::iterator_category
+
+ A synonym for \e {std::bidirectional_iterator_tag} indicating
+ this iterator is a bidirectional iterator.
+*/
+
+/*! \typedef JsonObject::iterator::reference
+
+ \internal
+*/
+
+/*! \typedef JsonObject::iterator::value_type
+
+ \internal
+*/
+
+/*! \fn JsonObject::iterator::iterator()
+
+ Constructs an uninitialized iterator.
+
+ Functions like key(), value(), and operator++() must not be
+ called on an uninitialized iterator. Use operator=() to assign a
+ value to it before using it.
+
+ \sa JsonObject::begin(), JsonObject::end()
+*/
+
+/*! \fn JsonObject::iterator::iterator(JsonObject *obj, int index)
+ \internal
+*/
+
+/*! \fn QString JsonObject::iterator::key() const
+
+ Returns the current item's key.
+
+ There is no direct way of changing an item's key through an
+ iterator, although it can be done by calling JsonObject::erase()
+ followed by JsonObject::insert().
+
+ \sa value()
+*/
+
+/*! \fn JsonValueRef JsonObject::iterator::value() const
+
+ Returns a modifiable reference to the current item's value.
+
+ You can change the value of an item by using value() on
+ the left side of an assignment.
+
+ The return value is of type JsonValueRef, a helper class for JsonArray
+ and JsonObject. When you get an object of type JsonValueRef, you can
+ use it as if it were a reference to a JsonValue. If you assign to it,
+ the assignment will apply to the element in the JsonArray or JsonObject
+ from which you got the reference.
+
+ \sa key(), operator*()
+*/
+
+/*! \fn JsonValueRef JsonObject::iterator::operator*() const
+
+ Returns a modifiable reference to the current item's value.
+
+ Same as value().
+
+ The return value is of type JsonValueRef, a helper class for JsonArray
+ and JsonObject. When you get an object of type JsonValueRef, you can
+ use it as if it were a reference to a JsonValue. If you assign to it,
+ the assignment will apply to the element in the JsonArray or JsonObject
+ from which you got the reference.
+
+ \sa key()
+*/
+
+/*! \fn JsonValueRef *JsonObject::iterator::operator->() const
+
+ Returns a pointer to a modifiable reference to the current item.
+*/
+
+/*!
+ \fn bool JsonObject::iterator::operator==(const iterator &other) const
+ \fn bool JsonObject::iterator::operator==(const const_iterator &other) const
+
+ Returns \c true if \a other points to the same item as this
+ iterator; otherwise returns \c false.
+
+ \sa operator!=()
+*/
+
+/*!
+ \fn bool JsonObject::iterator::operator!=(const iterator &other) const
+ \fn bool JsonObject::iterator::operator!=(const const_iterator &other) const
+
+ Returns \c true if \a other points to a different item than this
+ iterator; otherwise returns \c false.
+
+ \sa operator==()
+*/
+
+/*! \fn JsonObject::iterator JsonObject::iterator::operator++()
+
+ The prefix ++ operator, \c{++i}, advances the iterator to the
+ next item in the object and returns an iterator to the new current
+ item.
+
+ Calling this function on JsonObject::end() leads to undefined results.
+
+ \sa operator--()
+*/
+
+/*! \fn JsonObject::iterator JsonObject::iterator::operator++(int)
+
+ \overload
+
+ The postfix ++ operator, \c{i++}, advances the iterator to the
+ next item in the object and returns an iterator to the previously
+ current item.
+*/
+
+/*! \fn JsonObject::iterator JsonObject::iterator::operator--()
+
+ The prefix -- operator, \c{--i}, makes the preceding item
+ current and returns an iterator pointing to the new current item.
+
+ Calling this function on JsonObject::begin() leads to undefined
+ results.
+
+ \sa operator++()
+*/
+
+/*! \fn JsonObject::iterator JsonObject::iterator::operator--(int)
+
+ \overload
+
+ The postfix -- operator, \c{i--}, makes the preceding item
+ current and returns an iterator pointing to the previously
+ current item.
+*/
+
+/*! \fn JsonObject::iterator JsonObject::iterator::operator+(int j) const
+
+ Returns an iterator to the item at \a j positions forward from
+ this iterator. If \a j is negative, the iterator goes backward.
+
+ \sa operator-()
+
+*/
+
+/*! \fn JsonObject::iterator JsonObject::iterator::operator-(int j) const
+
+ Returns an iterator to the item at \a j positions backward from
+ this iterator. If \a j is negative, the iterator goes forward.
+
+ \sa operator+()
+*/
+
+/*! \fn JsonObject::iterator &JsonObject::iterator::operator+=(int j)
+
+ Advances the iterator by \a j items. If \a j is negative, the
+ iterator goes backward.
+
+ \sa operator-=(), operator+()
+*/
+
+/*! \fn JsonObject::iterator &JsonObject::iterator::operator-=(int j)
+
+ Makes the iterator go back by \a j items. If \a j is negative,
+ the iterator goes forward.
+
+ \sa operator+=(), operator-()
+*/
+
+/*!
+ \class JsonObject::const_iterator
+ \inmodule QtCore
+ \ingroup json
+ \since 5.0
+ \brief The JsonObject::const_iterator class provides an STL-style const iterator for JsonObject.
+
+ JsonObject::const_iterator allows you to iterate over a JsonObject.
+ If you want to modify the JsonObject as you iterate
+ over it, you must use JsonObject::iterator instead. It is generally
+ good practice to use JsonObject::const_iterator on a non-const JsonObject as
+ well, unless you need to change the JsonObject through the iterator.
+ Const iterators are slightly faster and improve code
+ readability.
+
+ The default JsonObject::const_iterator constructor creates an
+ uninitialized iterator. You must initialize it using a JsonObject
+ function like JsonObject::constBegin(), JsonObject::constEnd(), or
+ JsonObject::find() before you can start iterating.
+
+ Multiple iterators can be used on the same object. Existing iterators
+ will however become dangling if the object gets modified.
+
+ \sa JsonObject::iterator, {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*! \typedef JsonObject::const_iterator::difference_type
+
+ \internal
+*/
+
+/*! \typedef JsonObject::const_iterator::iterator_category
+
+ A synonym for \e {std::bidirectional_iterator_tag} indicating
+ this iterator is a bidirectional iterator.
+*/
+
+/*! \typedef JsonObject::const_iterator::reference
+
+ \internal
+*/
+
+/*! \typedef JsonObject::const_iterator::value_type
+
+ \internal
+*/
+
+/*! \fn JsonObject::const_iterator::const_iterator()
+
+ Constructs an uninitialized iterator.
+
+ Functions like key(), value(), and operator++() must not be
+ called on an uninitialized iterator. Use operator=() to assign a
+ value to it before using it.
+
+ \sa JsonObject::constBegin(), JsonObject::constEnd()
+*/
+
+/*! \fn JsonObject::const_iterator::const_iterator(const JsonObject *obj, int index)
+ \internal
+*/
+
+/*! \fn JsonObject::const_iterator::const_iterator(const iterator &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*! \fn QString JsonObject::const_iterator::key() const
+
+ Returns the current item's key.
+
+ \sa value()
+*/
+
+/*! \fn JsonValue JsonObject::const_iterator::value() const
+
+ Returns the current item's value.
+
+ \sa key(), operator*()
+*/
+
+/*! \fn JsonValue JsonObject::const_iterator::operator*() const
+
+ Returns the current item's value.
+
+ Same as value().
+
+ \sa key()
+*/
+
+/*! \fn JsonValue *JsonObject::const_iterator::operator->() const
+
+ Returns a pointer to the current item.
+*/
+
+/*! \fn bool JsonObject::const_iterator::operator==(const const_iterator &other) const
+ \fn bool JsonObject::const_iterator::operator==(const iterator &other) const
+
+ Returns \c true if \a other points to the same item as this
+ iterator; otherwise returns \c false.
+
+ \sa operator!=()
+*/
+
+/*! \fn bool JsonObject::const_iterator::operator!=(const const_iterator &other) const
+ \fn bool JsonObject::const_iterator::operator!=(const iterator &other) const
+
+ Returns \c true if \a other points to a different item than this
+ iterator; otherwise returns \c false.
+
+ \sa operator==()
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator++()
+
+ The prefix ++ operator, \c{++i}, advances the iterator to the
+ next item in the object and returns an iterator to the new current
+ item.
+
+ Calling this function on JsonObject::end() leads to undefined results.
+
+ \sa operator--()
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator++(int)
+
+ \overload
+
+ The postfix ++ operator, \c{i++}, advances the iterator to the
+ next item in the object and returns an iterator to the previously
+ current item.
+*/
+
+/*! \fn JsonObject::const_iterator &JsonObject::const_iterator::operator--()
+
+ The prefix -- operator, \c{--i}, makes the preceding item
+ current and returns an iterator pointing to the new current item.
+
+ Calling this function on JsonObject::begin() leads to undefined
+ results.
+
+ \sa operator++()
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator--(int)
+
+ \overload
+
+ The postfix -- operator, \c{i--}, makes the preceding item
+ current and returns an iterator pointing to the previously
+ current item.
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator+(int j) const
+
+ Returns an iterator to the item at \a j positions forward from
+ this iterator. If \a j is negative, the iterator goes backward.
+
+ This operation can be slow for large \a j values.
+
+ \sa operator-()
+*/
+
+/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator-(int j) const
+
+ Returns an iterator to the item at \a j positions backward from
+ this iterator. If \a j is negative, the iterator goes forward.
+
+ This operation can be slow for large \a j values.
+
+ \sa operator+()
+*/
+
+/*! \fn JsonObject::const_iterator &JsonObject::const_iterator::operator+=(int j)
+
+ Advances the iterator by \a j items. If \a j is negative, the
+ iterator goes backward.
+
+ This operation can be slow for large \a j values.
+
+ \sa operator-=(), operator+()
+*/
+
+/*! \fn JsonObject::const_iterator &JsonObject::const_iterator::operator-=(int j)
+
+ Makes the iterator go back by \a j items. If \a j is negative,
+ the iterator goes forward.
+
+ This operation can be slow for large \a j values.
+
+ \sa operator+=(), operator-()
+*/
+
+
+/*!
+ \internal
+ */
+void JsonObject::detach(uint32_t reserve)
+{
+ if (!d) {
+ d = new Internal::Data(reserve, JsonValue::Object);
+ o = static_cast<Internal::Object *>(d->header->root());
+ d->ref.ref();
+ return;
+ }
+ if (reserve == 0 && d->ref.load() == 1)
+ return;
+
+ Internal::Data *x = d->clone(o, reserve);
+ x->ref.ref();
+ if (!d->ref.deref())
+ delete d;
+ d = x;
+ o = static_cast<Internal::Object *>(d->header->root());
+}
+
+/*!
+ \internal
+ */
+void JsonObject::compact()
+{
+ if (!d || !d->compactionCounter)
+ return;
+
+ detach();
+ d->compact();
+ o = static_cast<Internal::Object *>(d->header->root());
+}
+
+/*!
+ \internal
+ */
+std::string JsonObject::keyAt(int i) const
+{
+ // assert(o && i >= 0 && i < (int)o->length);
+
+ Internal::Entry *e = o->entryAt(i);
+ return e->key();
+}
+
+/*!
+ \internal
+ */
+JsonValue JsonObject::valueAt(int i) const
+{
+ if (!o || i < 0 || i >= (int)o->length)
+ return JsonValue(JsonValue::Undefined);
+
+ Internal::Entry *e = o->entryAt(i);
+ return JsonValue(d, o, e->value);
+}
+
+/*!
+ \internal
+ */
+void JsonObject::setValueAt(int i, const JsonValue &val)
+{
+ // assert(o && i >= 0 && i < (int)o->length);
+
+ Internal::Entry *e = o->entryAt(i);
+ insert(e->key(), val);
+}
+
+
+/*! \class JsonDocument
+ \inmodule QtCore
+ \ingroup json
+ \ingroup shared
+ \reentrant
+ \since 5.0
+
+ \brief The JsonDocument class provides a way to read and write JSON documents.
+
+ JsonDocument 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 JsonDocument
+ using JsonDocument::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().
+
+ \sa {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*!
+ * Constructs an empty and invalid document.
+ */
+JsonDocument::JsonDocument()
+ : d(0)
+{
+}
+
+/*!
+ * Creates a JsonDocument from \a object.
+ */
+JsonDocument::JsonDocument(const JsonObject &object)
+ : d(0)
+{
+ setObject(object);
+}
+
+/*!
+ * Constructs a JsonDocument from \a array.
+ */
+JsonDocument::JsonDocument(const JsonArray &array)
+ : d(0)
+{
+ setArray(array);
+}
+
+/*!
+ \internal
+ */
+JsonDocument::JsonDocument(Internal::Data *data)
+ : d(data)
+{
+ // assert(d);
+ d->ref.ref();
+}
+
+/*!
+ Deletes the document.
+
+ Binary data set with fromRawData is not freed.
+ */
+JsonDocument::~JsonDocument()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ * Creates a copy of the \a other document.
+ */
+JsonDocument::JsonDocument(const JsonDocument &other)
+{
+ d = other.d;
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ * Assigns the \a other document to this JsonDocument.
+ * Returns a reference to this object.
+ */
+JsonDocument &JsonDocument::operator=(const JsonDocument &other)
+{
+ if (d != other.d) {
+ if (d && !d->ref.deref())
+ delete d;
+ d = other.d;
+ if (d)
+ d->ref.ref();
+ }
+
+ return *this;
+}
+
+/*! \enum JsonDocument::DataValidation
+
+ This value is used to tell JsonDocument whether to validate the binary data
+ when converting to a JsonDocument 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 JsonDocument 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 JsonDocument, JsonObject or JsonArray 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 JsonDocument representing the data.
+
+ \sa rawData(), fromBinaryData(), isNull(), DataValidation
+ */
+JsonDocument JsonDocument::fromRawData(const char *data, int size, DataValidation validation)
+{
+ if (std::uintptr_t(data) & 3) {
+ std::cerr <<"JsonDocument::fromRawData: data has to have 4 byte alignment\n";
+ return JsonDocument();
+ }
+
+ Internal::Data *d = new Internal::Data((char *)data, size);
+ d->ownsData = false;
+
+ if (validation != BypassValidation && !d->valid()) {
+ delete d;
+ return JsonDocument();
+ }
+
+ return JsonDocument(d);
+}
+
+/*!
+ Returns the raw binary representation of the data
+ \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.
+ */
+const char *JsonDocument::rawData(int *size) const
+{
+ if (!d) {
+ *size = 0;
+ return 0;
+ }
+ *size = d->alloc;
+ return d->rawData;
+}
+
+/*!
+ Creates a JsonDocument 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
+ */
+JsonDocument JsonDocument::fromBinaryData(const std::string &data, DataValidation validation)
+{
+ if (data.size() < (int)(sizeof(Internal::Header) + sizeof(Internal::Base)))
+ return JsonDocument();
+
+ Internal::Header h;
+ memcpy(&h, data.data(), sizeof(Internal::Header));
+ Internal::Base root;
+ memcpy(&root, data.data() + sizeof(Internal::Header), sizeof(Internal::Base));
+
+ // do basic checks here, so we don't try to allocate more memory than we can.
+ if (h.tag != JsonDocument::BinaryFormatTag || h.version != 1u ||
+ sizeof(Internal::Header) + root.size > (uint32_t)data.size())
+ return JsonDocument();
+
+ const uint32_t size = sizeof(Internal::Header) + root.size;
+ char *raw = (char *)malloc(size);
+ if (!raw)
+ return JsonDocument();
+
+ memcpy(raw, data.data(), size);
+ Internal::Data *d = new Internal::Data(raw, size);
+
+ if (validation != BypassValidation && !d->valid()) {
+ delete d;
+ return JsonDocument();
+ }
+
+ return JsonDocument(d);
+}
+
+/*!
+ \enum JsonDocument::JsonFormat
+
+ This value defines the format of the JSON byte array produced
+ when converting to a JsonDocument using toJson().
+
+ \value Indented Defines human readable output as follows:
+ \code
+ {
+ "Array": [
+ true,
+ 999,
+ "string"
+ ],
+ "Key": "Value",
+ "null": null
+ }
+ \endcode
+
+ \value Compact Defines a compact output as follows:
+ \code
+ {"Array":[true,999,"string"],"Key":"Value","null":null}
+ \endcode
+ */
+
+/*!
+ Converts the JsonDocument to a UTF-8 encoded JSON document in the provided \a format.
+
+ \sa fromJson(), JsonFormat
+ */
+#ifndef QT_JSON_READONLY
+std::string JsonDocument::toJson(JsonFormat format) const
+{
+ std::string json;
+
+ if (!d)
+ return json;
+
+ if (d->header->root()->isArray())
+ Internal::arrayToJson(static_cast<Internal::Array *>(d->header->root()), json, 0, (format == Compact));
+ else
+ Internal::objectToJson(static_cast<Internal::Object *>(d->header->root()), json, 0, (format == Compact));
+
+ return json;
+}
+#endif
+
+/*!
+ Parses a UTF-8 encoded JSON document and creates a JsonDocument
+ from it.
+
+ \a json contains the json document to be parsed.
+
+ The optional \a error variable can be used to pass in a JsonParseError data
+ structure that will contain information about possible errors encountered during
+ parsing.
+
+ \sa toJson(), JsonParseError
+ */
+JsonDocument JsonDocument::fromJson(const std::string &json, JsonParseError *error)
+{
+ Internal::Parser parser(json.data(), json.length());
+ return parser.parse(error);
+}
+
+/*!
+ Returns \c true if the document doesn't contain any data.
+ */
+bool JsonDocument::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()
+ */
+std::string JsonDocument::toBinaryData() const
+{
+ if (!d || !d->rawData)
+ return std::string();
+
+ return std::string(d->rawData, d->header->root()->size + sizeof(Internal::Header));
+}
+
+/*!
+ Returns \c true if the document contains an array.
+
+ \sa array(), isObject()
+ */
+bool JsonDocument::isArray() const
+{
+ if (!d)
+ return false;
+
+ Internal::Header *h = (Internal::Header *)d->rawData;
+ return h->root()->isArray();
+}
+
+/*!
+ Returns \c true if the document contains an object.
+
+ \sa object(), isArray()
+ */
+bool JsonDocument::isObject() const
+{
+ if (!d)
+ return false;
+
+ Internal::Header *h = (Internal::Header *)d->rawData;
+ return h->root()->isObject();
+}
+
+/*!
+ Returns the JsonObject contained in the document.
+
+ Returns an empty object if the document contains an
+ array.
+
+ \sa isObject(), array(), setObject()
+ */
+JsonObject JsonDocument::object() const
+{
+ if (d) {
+ Internal::Base *b = d->header->root();
+ if (b->isObject())
+ return JsonObject(d, static_cast<Internal::Object *>(b));
+ }
+ return JsonObject();
+}
+
+/*!
+ Returns the JsonArray contained in the document.
+
+ Returns an empty array if the document contains an
+ object.
+
+ \sa isArray(), object(), setArray()
+ */
+JsonArray JsonDocument::array() const
+{
+ if (d) {
+ Internal::Base *b = d->header->root();
+ if (b->isArray())
+ return JsonArray(d, static_cast<Internal::Array *>(b));
+ }
+ return JsonArray();
+}
+
+/*!
+ Sets \a object as the main object of this document.
+
+ \sa setArray(), object()
+ */
+void JsonDocument::setObject(const JsonObject &object)
+{
+ if (d && !d->ref.deref())
+ delete d;
+
+ d = object.d;
+
+ if (!d) {
+ d = new Internal::Data(0, JsonValue::Object);
+ } else if (d->compactionCounter || object.o != d->header->root()) {
+ JsonObject 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 JsonDocument::setArray(const JsonArray &array)
+{
+ if (d && !d->ref.deref())
+ delete d;
+
+ d = array.d;
+
+ if (!d) {
+ d = new Internal::Data(0, JsonValue::Array);
+ } else if (d->compactionCounter || array.a != d->header->root()) {
+ JsonArray 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 JsonDocument::operator==(const JsonDocument &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 JsonObject(d, static_cast<Internal::Object *>(d->header->root()))
+ == JsonObject(other.d, static_cast<Internal::Object *>(other.d->header->root()));
+ else
+ return JsonArray(d, static_cast<Internal::Array *>(d->header->root()))
+ == JsonArray(other.d, static_cast<Internal::Array *>(other.d->header->root()));
+}
+
+/*!
+ \fn bool JsonDocument::operator!=(const JsonDocument &other) const
+
+ returns \c true if \a other is not equal to this document
+ */
+
+/*!
+ returns \c 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 JsonDocument::isNull() const
+{
+ return (d == 0);
+}
+
+
+static void objectContentToJson(const Object *o, std::string &json, int indent, bool compact);
+static void arrayContentToJson(const Array *a, std::string &json, int indent, bool compact);
+
+static uint8_t hexdig(uint32_t u)
+{
+ return (u < 0xa ? '0' + u : 'a' + u - 0xa);
+}
+
+static std::string escapedString(const std::string &in)
+{
+ std::string ba;
+ ba.reserve(in.length());
+
+ auto src = in.begin();
+ auto end = in.end();
+
+ while (src != end) {
+ uint8_t u = (*src++);
+ if (u < 0x20 || u == 0x22 || u == 0x5c) {
+ ba.push_back('\\');
+ switch (u) {
+ case 0x22:
+ ba.push_back('"');
+ break;
+ case 0x5c:
+ ba.push_back('\\');
+ break;
+ case 0x8:
+ ba.push_back('b');
+ break;
+ case 0xc:
+ ba.push_back('f');
+ break;
+ case 0xa:
+ ba.push_back('n');
+ break;
+ case 0xd:
+ ba.push_back('r');
+ break;
+ case 0x9:
+ ba.push_back('t');
+ break;
+ default:
+ ba.push_back('u');
+ ba.push_back('0');
+ ba.push_back('0');
+ ba.push_back(hexdig(u>>4));
+ ba.push_back(hexdig(u & 0xf));
+ }
+ } else {
+ ba.push_back(u);
+ }
+ }
+
+ return ba;
+}
+
+static void valueToJson(const Base *b, const Value &v, std::string &json, int indent, bool compact)
+{
+ JsonValue::Type type = (JsonValue::Type)(uint32_t)v.type;
+ switch (type) {
+ case JsonValue::Bool:
+ json += v.toBoolean() ? "true" : "false";
+ break;
+ case JsonValue::Double: {
+ const double d = v.toDouble(b);
+ if (std::isfinite(d)) {
+ // +2 to format to ensure the expected precision
+ const int n = std::numeric_limits<double>::digits10 + 2;
+ char buf[30] = {0};
+ sprintf(buf, "%.*g", n, d);
+ // Hack:
+ if (buf[0] == '-' && buf[1] == '0' && buf[2] == '\0')
+ json += "0";
+ else
+ json += buf;
+ } else {
+ json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
+ }
+ break;
+ }
+ case JsonValue::String:
+ json += '"';
+ json += escapedString(v.toString(b));
+ json += '"';
+ break;
+ case JsonValue::Array:
+ json += compact ? "[" : "[\n";
+ arrayContentToJson(static_cast<Array *>(v.base(b)), json, indent + (compact ? 0 : 1), compact);
+ json += std::string(4*indent, ' ');
+ json += ']';
+ break;
+ case JsonValue::Object:
+ json += compact ? "{" : "{\n";
+ objectContentToJson(static_cast<Object *>(v.base(b)), json, indent + (compact ? 0 : 1), compact);
+ json += std::string(4*indent, ' ');
+ json += '}';
+ break;
+ case JsonValue::Null:
+ default:
+ json += "null";
+ }
+}
+
+static void arrayContentToJson(const Array *a, std::string &json, int indent, bool compact)
+{
+ if (!a || !a->length)
+ return;
+
+ std::string indentString(4*indent, ' ');
+
+ uint32_t i = 0;
+ while (1) {
+ json += indentString;
+ valueToJson(a, a->at(i), json, indent, compact);
+
+ if (++i == a->length) {
+ if (!compact)
+ json += '\n';
+ break;
+ }
+
+ json += compact ? "," : ",\n";
+ }
+}
+
+static void objectContentToJson(const Object *o, std::string &json, int indent, bool compact)
+{
+ if (!o || !o->length)
+ return;
+
+ std::string indentString(4*indent, ' ');
+
+ uint32_t i = 0;
+ while (1) {
+ Entry *e = o->entryAt(i);
+ json += indentString;
+ json += '"';
+ json += escapedString(e->key());
+ json += compact ? "\":" : "\": ";
+ valueToJson(o, e->value, json, indent, compact);
+
+ if (++i == o->length) {
+ if (!compact)
+ json += '\n';
+ break;
+ }
+
+ json += compact ? "," : ",\n";
+ }
+}
+
+namespace Internal {
+
+void objectToJson(const Object *o, std::string &json, int indent, bool compact)
+{
+ json.reserve(json.size() + (o ? (int)o->size : 16));
+ json += compact ? "{" : "{\n";
+ objectContentToJson(o, json, indent + (compact ? 0 : 1), compact);
+ json += std::string(4*indent, ' ');
+ json += compact ? "}" : "}\n";
+}
+
+void arrayToJson(const Array *a, std::string &json, int indent, bool compact)
+{
+ json.reserve(json.size() + (a ? (int)a->size : 16));
+ json += compact ? "[" : "[\n";
+ arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact);
+ json += std::string(4*indent, ' ');
+ json += compact ? "]" : "]\n";
+}
+
+}
+
+
+
+/*!
+ \class JsonParseError
+ \inmodule QtCore
+ \ingroup json
+ \ingroup shared
+ \reentrant
+ \since 5.0
+
+ \brief The JsonParseError class is used to report errors during JSON parsing.
+
+ \sa {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*!
+ \enum JsonParseError::ParseError
+
+ This enum describes the type of error that occurred during the parsing of a JSON document.
+
+ \value NoError No error occurred
+ \value UnterminatedObject An object is not correctly terminated with a closing curly bracket
+ \value MissingNameSeparator A comma separating different items is missing
+ \value UnterminatedArray The array is not correctly terminated with a closing square bracket
+ \value MissingValueSeparator A colon separating keys from values inside objects is missing
+ \value IllegalValue The value is illegal
+ \value TerminationByNumber The input stream ended while parsing a number
+ \value IllegalNumber The number is not well formed
+ \value IllegalEscapeSequence An illegal escape sequence occurred in the input
+ \value IllegalUTF8String An illegal UTF8 sequence occurred in the input
+ \value UnterminatedString A string wasn't terminated with a quote
+ \value MissingObject An object was expected but couldn't be found
+ \value DeepNesting The JSON document is too deeply nested for the parser to parse it
+ \value DocumentTooLarge The JSON document is too large for the parser to parse it
+ \value GarbageAtEnd The parsed document contains additional garbage characters at the end
+
+*/
+
+/*!
+ \variable JsonParseError::error
+
+ Contains the type of the parse error. Is equal to JsonParseError::NoError if the document
+ was parsed correctly.
+
+ \sa ParseError, errorString()
+*/
+
+
+/*!
+ \variable JsonParseError::offset
+
+ Contains the offset in the input string where the parse error occurred.
+
+ \sa error, errorString()
+*/
+
+using namespace Internal;
+
+Parser::Parser(const char *json, int length)
+ : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(JsonParseError::NoError)
+{
+ end = json + length;
+}
+
+
+
+/*
+
+begin-array = ws %x5B ws ; [ left square bracket
+
+begin-object = ws %x7B ws ; { left curly bracket
+
+end-array = ws %x5D ws ; ] right square bracket
+
+end-object = ws %x7D ws ; } right curly bracket
+
+name-separator = ws %x3A ws ; : colon
+
+value-separator = ws %x2C ws ; , comma
+
+Insignificant whitespace is allowed before or after any of the six
+structural characters.
+
+ws = *(
+ %x20 / ; Space
+ %x09 / ; Horizontal tab
+ %x0A / ; Line feed or New line
+ %x0D ; Carriage return
+ )
+
+*/
+
+enum {
+ Space = 0x20,
+ Tab = 0x09,
+ LineFeed = 0x0a,
+ Return = 0x0d,
+ BeginArray = 0x5b,
+ BeginObject = 0x7b,
+ EndArray = 0x5d,
+ EndObject = 0x7d,
+ NameSeparator = 0x3a,
+ ValueSeparator = 0x2c,
+ Quote = 0x22
+};
+
+void Parser::eatBOM()
+{
+ // eat UTF-8 byte order mark
+ if (end - json > 3
+ && (unsigned char)json[0] == 0xef
+ && (unsigned char)json[1] == 0xbb
+ && (unsigned char)json[2] == 0xbf)
+ json += 3;
+}
+
+bool Parser::eatSpace()
+{
+ while (json < end) {
+ if (*json > Space)
+ break;
+ if (*json != Space &&
+ *json != Tab &&
+ *json != LineFeed &&
+ *json != Return)
+ break;
+ ++json;
+ }
+ return (json < end);
+}
+
+char Parser::nextToken()
+{
+ if (!eatSpace())
+ return 0;
+ char token = *json++;
+ switch (token) {
+ case BeginArray:
+ case BeginObject:
+ case NameSeparator:
+ case ValueSeparator:
+ case EndArray:
+ case EndObject:
+ eatSpace();
+ case Quote:
+ break;
+ default:
+ token = 0;
+ break;
+ }
+ return token;
+}
+
+/*
+ JSON-text = object / array
+*/
+JsonDocument Parser::parse(JsonParseError *error)
+{
+#ifdef PARSER_DEBUG
+ indent = 0;
+ std::cerr << ">>>>> parser begin";
+#endif
+ // allocate some space
+ dataLength = std::max(end - json, std::ptrdiff_t(256));
+ data = (char *)malloc(dataLength);
+
+ // fill in Header data
+ Header *h = (Header *)data;
+ h->tag = JsonDocument::BinaryFormatTag;
+ h->version = 1u;
+
+ current = sizeof(Header);
+
+ eatBOM();
+ char token = nextToken();
+
+ DEBUG << std::hex << (uint32_t)token;
+ if (token == BeginArray) {
+ if (!parseArray())
+ goto error;
+ } else if (token == BeginObject) {
+ if (!parseObject())
+ goto error;
+ } else {
+ lastError = JsonParseError::IllegalValue;
+ goto error;
+ }
+
+ eatSpace();
+ if (json < end) {
+ lastError = JsonParseError::GarbageAtEnd;
+ goto error;
+ }
+
+ END;
+ {
+ if (error) {
+ error->offset = 0;
+ error->error = JsonParseError::NoError;
+ }
+ Data *d = new Data(data, current);
+ return JsonDocument(d);
+ }
+
+error:
+#ifdef PARSER_DEBUG
+ std::cerr << ">>>>> parser error";
+#endif
+ if (error) {
+ error->offset = json - head;
+ error->error = lastError;
+ }
+ free(data);
+ return JsonDocument();
+}
+
+
+void Parser::ParsedObject::insert(uint32_t offset)
+{
+ const Entry *newEntry = reinterpret_cast<const Entry *>(parser->data + objectPosition + offset);
+ size_t min = 0;
+ size_t n = offsets.size();
+ while (n > 0) {
+ int half = n >> 1;
+ int middle = min + half;
+ if (*entryAt(middle) >= *newEntry) {
+ n = half;
+ } else {
+ min = middle + 1;
+ n -= half + 1;
+ }
+ }
+ if (min < offsets.size() && *entryAt(min) == *newEntry) {
+ offsets[min] = offset;
+ } else {
+ offsets.insert(offsets.begin() + min, offset);
+ }
+}
+
+/*
+ object = begin-object [ member *( value-separator member ) ]
+ end-object
+*/
+
+bool Parser::parseObject()
+{
+ if (++nestingLevel > nestingLimit) {
+ lastError = JsonParseError::DeepNesting;
+ return false;
+ }
+
+ int objectOffset = reserveSpace(sizeof(Object));
+ BEGIN << "parseObject pos=" << objectOffset << current << json;
+
+ ParsedObject parsedObject(this, objectOffset);
+
+ char token = nextToken();
+ while (token == Quote) {
+ int off = current - objectOffset;
+ if (!parseMember(objectOffset))
+ return false;
+ parsedObject.insert(off);
+ token = nextToken();
+ if (token != ValueSeparator)
+ break;
+ token = nextToken();
+ if (token == EndObject) {
+ lastError = JsonParseError::MissingObject;
+ return false;
+ }
+ }
+
+ DEBUG << "end token=" << token;
+ if (token != EndObject) {
+ lastError = JsonParseError::UnterminatedObject;
+ return false;
+ }
+
+ DEBUG << "numEntries" << parsedObject.offsets.size();
+ int table = objectOffset;
+ // finalize the object
+ if (parsedObject.offsets.size()) {
+ int tableSize = parsedObject.offsets.size()*sizeof(uint32_t);
+ table = reserveSpace(tableSize);
+ memcpy(data + table, &*parsedObject.offsets.begin(), tableSize);
+ }
+
+ Object *o = (Object *)(data + objectOffset);
+ o->tableOffset = table - objectOffset;
+ o->size = current - objectOffset;
+ o->is_object = true;
+ o->length = parsedObject.offsets.size();
+
+ DEBUG << "current=" << current;
+ END;
+
+ --nestingLevel;
+ return true;
+}
+
+/*
+ member = string name-separator value
+*/
+bool Parser::parseMember(int baseOffset)
+{
+ int entryOffset = reserveSpace(sizeof(Entry));
+ BEGIN << "parseMember pos=" << entryOffset;
+
+ if (!parseString())
+ return false;
+ char token = nextToken();
+ if (token != NameSeparator) {
+ lastError = JsonParseError::MissingNameSeparator;
+ return false;
+ }
+ Value val;
+ if (!parseValue(&val, baseOffset))
+ return false;
+
+ // finalize the entry
+ Entry *e = (Entry *)(data + entryOffset);
+ e->value = val;
+
+ END;
+ return true;
+}
+
+/*
+ array = begin-array [ value *( value-separator value ) ] end-array
+*/
+bool Parser::parseArray()
+{
+ BEGIN << "parseArray";
+
+ if (++nestingLevel > nestingLimit) {
+ lastError = JsonParseError::DeepNesting;
+ return false;
+ }
+
+ int arrayOffset = reserveSpace(sizeof(Array));
+
+ std::vector<Value> values;
+ values.reserve(64);
+
+ if (!eatSpace()) {
+ lastError = JsonParseError::UnterminatedArray;
+ return false;
+ }
+ if (*json == EndArray) {
+ nextToken();
+ } else {
+ while (1) {
+ Value val;
+ if (!parseValue(&val, arrayOffset))
+ return false;
+ values.push_back(val);
+ char token = nextToken();
+ if (token == EndArray)
+ break;
+ else if (token != ValueSeparator) {
+ if (!eatSpace())
+ lastError = JsonParseError::UnterminatedArray;
+ else
+ lastError = JsonParseError::MissingValueSeparator;
+ return false;
+ }
+ }
+ }
+
+ DEBUG << "size =" << values.size();
+ int table = arrayOffset;
+ // finalize the object
+ if (values.size()) {
+ int tableSize = values.size()*sizeof(Value);
+ table = reserveSpace(tableSize);
+ memcpy(data + table, values.data(), tableSize);
+ }
+
+ Array *a = (Array *)(data + arrayOffset);
+ a->tableOffset = table - arrayOffset;
+ a->size = current - arrayOffset;
+ a->is_object = false;
+ a->length = values.size();
+
+ DEBUG << "current=" << current;
+ END;
+
+ --nestingLevel;
+ return true;
+}
+
+/*
+value = false / null / true / object / array / number / string
+
+*/
+
+bool Parser::parseValue(Value *val, int baseOffset)
+{
+ BEGIN << "parse Value" << json;
+ val->_dummy = 0;
+
+ switch (*json++) {
+ case 'n':
+ if (end - json < 4) {
+ lastError = JsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'u' &&
+ *json++ == 'l' &&
+ *json++ == 'l') {
+ val->type = JsonValue::Null;
+ DEBUG << "value: null";
+ END;
+ return true;
+ }
+ lastError = JsonParseError::IllegalValue;
+ return false;
+ case 't':
+ if (end - json < 4) {
+ lastError = JsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'r' &&
+ *json++ == 'u' &&
+ *json++ == 'e') {
+ val->type = JsonValue::Bool;
+ val->value = true;
+ DEBUG << "value: true";
+ END;
+ return true;
+ }
+ lastError = JsonParseError::IllegalValue;
+ return false;
+ case 'f':
+ if (end - json < 5) {
+ lastError = JsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'a' &&
+ *json++ == 'l' &&
+ *json++ == 's' &&
+ *json++ == 'e') {
+ val->type = JsonValue::Bool;
+ val->value = false;
+ DEBUG << "value: false";
+ END;
+ return true;
+ }
+ lastError = JsonParseError::IllegalValue;
+ return false;
+ case Quote: {
+ val->type = JsonValue::String;
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = JsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = current - baseOffset;
+ if (!parseString())
+ return false;
+ val->intValue = false;
+ DEBUG << "value: string";
+ END;
+ return true;
+ }
+ case BeginArray:
+ val->type = JsonValue::Array;
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = JsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = current - baseOffset;
+ if (!parseArray())
+ return false;
+ DEBUG << "value: array";
+ END;
+ return true;
+ case BeginObject:
+ val->type = JsonValue::Object;
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = JsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = current - baseOffset;
+ if (!parseObject())
+ return false;
+ DEBUG << "value: object";
+ END;
+ return true;
+ case EndArray:
+ lastError = JsonParseError::MissingObject;
+ return false;
+ default:
+ --json;
+ if (!parseNumber(val, baseOffset))
+ return false;
+ DEBUG << "value: number";
+ END;
+ }
+
+ return true;
+}
+
+
+
+
+
+/*
+ number = [ minus ] int [ frac ] [ exp ]
+ decimal-point = %x2E ; .
+ digit1-9 = %x31-39 ; 1-9
+ e = %x65 / %x45 ; e E
+ exp = e [ minus / plus ] 1*DIGIT
+ frac = decimal-point 1*DIGIT
+ int = zero / ( digit1-9 *DIGIT )
+ minus = %x2D ; -
+ plus = %x2B ; +
+ zero = %x30 ; 0
+
+*/
+
+bool Parser::parseNumber(Value *val, int baseOffset)
+{
+ BEGIN << "parseNumber" << json;
+ val->type = JsonValue::Double;
+
+ const char *start = json;
+ bool isInt = true;
+
+ // minus
+ if (json < end && *json == '-')
+ ++json;
+
+ // int = zero / ( digit1-9 *DIGIT )
+ if (json < end && *json == '0') {
+ ++json;
+ } else {
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ // frac = decimal-point 1*DIGIT
+ if (json < end && *json == '.') {
+ isInt = false;
+ ++json;
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ // exp = e [ minus / plus ] 1*DIGIT
+ if (json < end && (*json == 'e' || *json == 'E')) {
+ isInt = false;
+ ++json;
+ if (json < end && (*json == '-' || *json == '+'))
+ ++json;
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ if (json >= end) {
+ lastError = JsonParseError::TerminationByNumber;
+ return false;
+ }
+
+ if (isInt) {
+ char *endptr = const_cast<char *>(json);
+ long long int n = strtoll(start, &endptr, 0);
+ if (endptr != start && n < (1<<25) && n > -(1<<25)) {
+ val->int_value = n;
+ val->intValue = true;
+ END;
+ return true;
+ }
+ }
+
+ char *endptr = const_cast<char *>(json);
+ double d = strtod(start, &endptr);
+
+ if (start == endptr || isinf(d)) {
+ lastError = JsonParseError::IllegalNumber;
+ return false;
+ }
+
+ int pos = reserveSpace(sizeof(double));
+ memcpy(data + pos, &d, sizeof(double));
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = JsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = pos - baseOffset;
+ val->intValue = false;
+
+ END;
+ return true;
+}
+
+/*
+
+ string = quotation-mark *char quotation-mark
+
+ char = unescaped /
+ escape (
+ %x22 / ; " quotation mark U+0022
+ %x5C / ; \ reverse solidus U+005C
+ %x2F / ; / solidus U+002F
+ %x62 / ; b backspace U+0008
+ %x66 / ; f form feed U+000C
+ %x6E / ; n line feed U+000A
+ %x72 / ; r carriage return U+000D
+ %x74 / ; t tab U+0009
+ %x75 4HEXDIG ) ; uXXXX U+XXXX
+
+ escape = %x5C ; \
+
+ quotation-mark = %x22 ; "
+
+ unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ */
+static bool addHexDigit(char digit, uint32_t *result)
+{
+ *result <<= 4;
+ if (digit >= '0' && digit <= '9')
+ *result |= (digit - '0');
+ else if (digit >= 'a' && digit <= 'f')
+ *result |= (digit - 'a') + 10;
+ else if (digit >= 'A' && digit <= 'F')
+ *result |= (digit - 'A') + 10;
+ else
+ return false;
+ return true;
+}
+
+static bool scanEscapeSequence(const char *&json, const char *end, std::string *out)
+{
+ ++json;
+ if (json >= end)
+ return false;
+
+ DEBUG << "scan escape" << (char)*json;
+ char escaped = *json++;
+ switch (escaped) {
+ case '"':
+ out->push_back('"'); break;
+ case '\\':
+ out->push_back('\\'); break;
+ case '/':
+ out->push_back('/'); break;
+ case 'b':
+ out->push_back(0x8); break;
+ case 'f':
+ out->push_back(0xc); break;
+ case 'n':
+ out->push_back(0xa); break;
+ case 'r':
+ out->push_back(0xd); break;
+ case 't':
+ out->push_back(0x9); break;
+ case 'u': {
+ uint32_t c = 0;
+ if (json > end - 4)
+ return false;
+ for (int i = 0; i < 4; ++i) {
+ if (!addHexDigit(*json, &c))
+ return false;
+ ++json;
+ }
+ if (c < 0x80) {
+ out->push_back(c);
+ return true;
+ }
+ if (c < 0x800) {
+ out->push_back(192 + c / 64);
+ out->push_back(128 + c % 64);
+ return true;
+ }
+ if (c - 0xd800u < 0x800) {
+ return false;
+ }
+ if (c < 0x10000) {
+ out->push_back(224 + c / 4096);
+ out->push_back(128 + c / 64 % 64);
+ out->push_back(128 + c % 64);
+ return true;
+ }
+ if (c < 0x110000) {
+ out->push_back(240 + c / 262144);
+ out->push_back(128 + c / 4096 % 64);
+ out->push_back(128 + c / 64 % 64);
+ out->push_back(128 + c % 64);
+ return true;
+ }
+ return false;
+ }
+ default:
+ // this is not as strict as one could be, but allows for more Json files
+ // to be parsed correctly.
+ out->push_back(escaped);
+ return true;
+ }
+ return true;
+}
+
+bool Parser::parseString()
+{
+ const char *start = json;
+ const int outStart = current;
+ int stringPos = reserveSpace(2);
+
+ json = start;
+ current = outStart + sizeof(int);
+
+// const size_t insize = end - json;
+ std::string out;
+ out.reserve(100);
+
+ while (json < end) {
+ if (*json == '"')
+ break;
+ else if (*json == '\\') {
+ if (!scanEscapeSequence(json, end, &out)) {
+ lastError = JsonParseError::IllegalEscapeSequence;
+ return false;
+ }
+ } else {
+ out.push_back(*json++);
+ }
+ }
+ ++json;
+
+ const int pos = reserveSpace(out.size());
+ memcpy(data + pos, out.data(), out.size());
+
+ if (json >= end) {
+ lastError = JsonParseError::UnterminatedString;
+ return false;
+ }
+
+ // write string length and padding.
+ *(int *)(data + stringPos) = out.size();
+ reserveSpace((4 - current) & 3);
+
+ END;
+ return true;
+}
+
+namespace Internal {
+
+static const Base emptyArray = { sizeof(Base), { 0 }, 0 };
+static const Base emptyObject = { sizeof(Base), { 0 }, 0 };
+
+
+void Data::compact()
+{
+ // assert(sizeof(Value) == sizeof(offset));
+
+ if (!compactionCounter)
+ return;
+
+ Base *base = header->root();
+ int reserve = 0;
+ if (base->is_object) {
+ Object *o = static_cast<Object *>(base);
+ for (int i = 0; i < (int)o->length; ++i)
+ reserve += o->entryAt(i)->usedStorage(o);
+ } else {
+ Array *a = static_cast<Array *>(base);
+ for (int i = 0; i < (int)a->length; ++i)
+ reserve += (*a)[i].usedStorage(a);
+ }
+
+ int size = sizeof(Base) + reserve + base->length*sizeof(offset);
+ int alloc = sizeof(Header) + size;
+ Header *h = (Header *) malloc(alloc);
+ h->tag = JsonDocument::BinaryFormatTag;
+ h->version = 1;
+ Base *b = h->root();
+ b->size = size;
+ b->is_object = header->root()->is_object;
+ b->length = base->length;
+ b->tableOffset = reserve + sizeof(Array);
+
+ int offset = sizeof(Base);
+ if (b->is_object) {
+ Object *o = static_cast<Object *>(base);
+ Object *no = static_cast<Object *>(b);
+
+ for (int i = 0; i < (int)o->length; ++i) {
+ no->table()[i] = offset;
+
+ const Entry *e = o->entryAt(i);
+ Entry *ne = no->entryAt(i);
+ int s = e->size();
+ memcpy(ne, e, s);
+ offset += s;
+ int dataSize = e->value.usedStorage(o);
+ if (dataSize) {
+ memcpy((char *)no + offset, e->value.data(o), dataSize);
+ ne->value.value = offset;
+ offset += dataSize;
+ }
+ }
+ } else {
+ Array *a = static_cast<Array *>(base);
+ Array *na = static_cast<Array *>(b);
+
+ for (int i = 0; i < (int)a->length; ++i) {
+ const Value &v = (*a)[i];
+ Value &nv = (*na)[i];
+ nv = v;
+ int dataSize = v.usedStorage(a);
+ if (dataSize) {
+ memcpy((char *)na + offset, v.data(a), dataSize);
+ nv.value = offset;
+ offset += dataSize;
+ }
+ }
+ }
+ // assert(offset == (int)b->tableOffset);
+
+ free(header);
+ header = h;
+ this->alloc = alloc;
+ compactionCounter = 0;
+}
+
+bool Data::valid() const
+{
+ if (header->tag != JsonDocument::BinaryFormatTag || header->version != 1u)
+ return false;
+
+ bool res = false;
+ if (header->root()->is_object)
+ res = static_cast<Object *>(header->root())->isValid();
+ else
+ res = static_cast<Array *>(header->root())->isValid();
+
+ return res;
+}
+
+
+int Base::reserveSpace(uint32_t dataSize, int posInTable, uint32_t numItems, bool replace)
+{
+ // assert(posInTable >= 0 && posInTable <= (int)length);
+ if (size + dataSize >= Value::MaxSize) {
+ fprintf(stderr, "Json: Document too large to store in data structure %d %d %d\n", (uint32_t)size, dataSize, Value::MaxSize);
+ return 0;
+ }
+
+ offset off = tableOffset;
+ // move table to new position
+ if (replace) {
+ memmove((char *)(table()) + dataSize, table(), length*sizeof(offset));
+ } else {
+ memmove((char *)(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable)*sizeof(offset));
+ memmove((char *)(table()) + dataSize, table(), posInTable*sizeof(offset));
+ }
+ tableOffset += dataSize;
+ for (int i = 0; i < (int)numItems; ++i)
+ table()[posInTable + i] = off;
+ size += dataSize;
+ if (!replace) {
+ length += numItems;
+ size += numItems * sizeof(offset);
+ }
+ return off;
+}
+
+void Base::removeItems(int pos, int numItems)
+{
+ // assert(pos >= 0 && pos <= (int)length);
+ if (pos + numItems < (int)length)
+ memmove(table() + pos, table() + pos + numItems, (length - pos - numItems)*sizeof(offset));
+ length -= numItems;
+}
+
+int Object::indexOf(const std::string &key, bool *exists)
+{
+ int min = 0;
+ int n = length;
+ while (n > 0) {
+ int half = n >> 1;
+ int middle = min + half;
+ if (*entryAt(middle) >= key) {
+ n = half;
+ } else {
+ min = middle + 1;
+ n -= half + 1;
+ }
+ }
+ if (min < (int)length && *entryAt(min) == key) {
+ *exists = true;
+ return min;
+ }
+ *exists = false;
+ return min;
+}
+
+bool Object::isValid() const
+{
+ if (tableOffset + length*sizeof(offset) > size)
+ return false;
+
+ std::string lastKey;
+ for (uint32_t i = 0; i < length; ++i) {
+ offset entryOffset = table()[i];
+ if (entryOffset + sizeof(Entry) >= tableOffset)
+ return false;
+ Entry *e = entryAt(i);
+ int s = e->size();
+ if (table()[i] + s > tableOffset)
+ return false;
+ std::string key = e->key();
+ if (key < lastKey)
+ return false;
+ if (!e->value.isValid(this))
+ return false;
+ lastKey = key;
+ }
+ return true;
+}
+
+bool Array::isValid() const
+{
+ if (tableOffset + length*sizeof(offset) > size)
+ return false;
+
+ for (uint32_t i = 0; i < length; ++i) {
+ if (!at(i).isValid(this))
+ return false;
+ }
+ return true;
+}
+
+
+bool Entry::operator==(const std::string &key) const
+{
+ return shallowKey() == key;
+}
+
+bool Entry::operator==(const Entry &other) const
+{
+ return shallowKey() == other.shallowKey();
+}
+
+bool Entry::operator>=(const Entry &other) const
+{
+ return shallowKey() >= other.shallowKey();
+}
+
+
+int Value::usedStorage(const Base *b) const
+{
+ int s = 0;
+ switch (type) {
+ case JsonValue::Double:
+ if (intValue)
+ break;
+ s = sizeof(double);
+ break;
+ case JsonValue::String: {
+ char *d = data(b);
+ s = sizeof(int) + (*(int *)d);
+ break;
+ }
+ case JsonValue::Array:
+ case JsonValue::Object:
+ s = base(b)->size;
+ break;
+ case JsonValue::Null:
+ case JsonValue::Bool:
+ default:
+ break;
+ }
+ return alignedSize(s);
+}
+
+bool Value::isValid(const Base *b) const
+{
+ int offset = 0;
+ switch (type) {
+ case JsonValue::Double:
+ if (intValue)
+ break;
+ // fall through
+ case JsonValue::String:
+ case JsonValue::Array:
+ case JsonValue::Object:
+ offset = value;
+ break;
+ case JsonValue::Null:
+ case JsonValue::Bool:
+ default:
+ break;
+ }
+
+ if (!offset)
+ return true;
+ if (offset + sizeof(uint32_t) > b->tableOffset)
+ return false;
+
+ int s = usedStorage(b);
+ if (!s)
+ return true;
+ if (s < 0 || offset + s > (int)b->tableOffset)
+ return false;
+ if (type == JsonValue::Array)
+ return static_cast<Array *>(base(b))->isValid();
+ if (type == JsonValue::Object)
+ return static_cast<Object *>(base(b))->isValid();
+ return true;
+}
+
+/*!
+ \internal
+ */
+int Value::requiredStorage(JsonValue &v, bool *compressed)
+{
+ *compressed = false;
+ switch (v.t) {
+ case JsonValue::Double:
+ if (Internal::compressedNumber(v.dbl) != INT_MAX) {
+ *compressed = true;
+ return 0;
+ }
+ return sizeof(double);
+ case JsonValue::String: {
+ std::string s = v.toString().data();
+ *compressed = false;
+ return Internal::qStringSize(s);
+ }
+ case JsonValue::Array:
+ case JsonValue::Object:
+ if (v.d && v.d->compactionCounter) {
+ v.detach();
+ v.d->compact();
+ v.base = static_cast<Internal::Base *>(v.d->header->root());
+ }
+ return v.base ? v.base->size : sizeof(Internal::Base);
+ case JsonValue::Undefined:
+ case JsonValue::Null:
+ case JsonValue::Bool:
+ break;
+ }
+ return 0;
+}
+
+/*!
+ \internal
+ */
+uint32_t Value::valueToStore(const JsonValue &v, uint32_t offset)
+{
+ switch (v.t) {
+ case JsonValue::Undefined:
+ case JsonValue::Null:
+ break;
+ case JsonValue::Bool:
+ return v.b;
+ case JsonValue::Double: {
+ int c = Internal::compressedNumber(v.dbl);
+ if (c != INT_MAX)
+ return c;
+ }
+ // fall through
+ case JsonValue::String:
+ case JsonValue::Array:
+ case JsonValue::Object:
+ return offset;
+ }
+ return 0;
+}
+
+/*!
+ \internal
+ */
+
+void Value::copyData(const JsonValue &v, char *dest, bool compressed)
+{
+ switch (v.t) {
+ case JsonValue::Double:
+ if (!compressed)
+ memcpy(dest, &v.ui, 8);
+ break;
+ case JsonValue::String: {
+ std::string str = v.toString();
+ Internal::copyString(dest, str);
+ break;
+ }
+ case JsonValue::Array:
+ case JsonValue::Object: {
+ const Internal::Base *b = v.base;
+ if (!b)
+ b = (v.t == JsonValue::Array ? &emptyArray : &emptyObject);
+ memcpy(dest, b, b->size);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+} // namespace Internal
+} // namespace Json
diff --git a/src/shared/json/json.h b/src/shared/json/json.h
new file mode 100644
index 00000000000..7bc945cd3e9
--- /dev/null
+++ b/src/shared/json/json.h
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef JSONVALUE_H
+#define JSONVALUE_H
+
+#include <initializer_list>
+#include <string>
+#include <vector>
+
+namespace Json {
+
+class JsonArray;
+class JsonObject;
+
+namespace Internal {
+class Data;
+class Base;
+class Object;
+class Header;
+class Array;
+class Value;
+class Entry;
+class SharedString;
+class Parser;
+}
+
+class JsonValue
+{
+public:
+ enum Type {
+ Null = 0x0,
+ Bool = 0x1,
+ Double = 0x2,
+ String = 0x3,
+ Array = 0x4,
+ Object = 0x5,
+ Undefined = 0x80
+ };
+
+ JsonValue(Type = Null);
+ JsonValue(bool b);
+ JsonValue(double n);
+ JsonValue(int n);
+ JsonValue(int64_t n);
+ JsonValue(const std::string &s);
+ JsonValue(const char *s);
+ JsonValue(const JsonArray &a);
+ JsonValue(const JsonObject &o);
+
+ ~JsonValue();
+
+ JsonValue(const JsonValue &other);
+ JsonValue &operator =(const JsonValue &other);
+
+ Type type() const { return t; }
+ bool isNull() const { return t == Null; }
+ bool isBool() const { return t == Bool; }
+ bool isDouble() const { return t == Double; }
+ bool isString() const { return t == String; }
+ bool isArray() const { return t == Array; }
+ bool isObject() const { return t == Object; }
+ bool isUndefined() const { return t == Undefined; }
+
+ bool toBool(bool defaultValue = false) const;
+ int toInt(int defaultValue = 0) const;
+ double toDouble(double defaultValue = 0) const;
+ std::string toString(const std::string &defaultValue = std::string()) const;
+ JsonArray toArray() const;
+ JsonArray toArray(const JsonArray &defaultValue) const;
+ JsonObject toObject() const;
+ JsonObject toObject(const JsonObject &defaultValue) const;
+
+ bool operator==(const JsonValue &other) const;
+ bool operator!=(const JsonValue &other) const;
+
+private:
+ // avoid implicit conversions from char * to bool
+ JsonValue(const void *) : t(Null) {}
+ friend class Internal::Value;
+ friend class JsonArray;
+ friend class JsonObject;
+
+ JsonValue(Internal::Data *d, Internal::Base *b, const Internal::Value& v);
+
+ void detach();
+
+ union {
+ uint64_t ui;
+ bool b;
+ double dbl;
+ Internal::SharedString *stringData;
+ Internal::Base *base;
+ };
+ Internal::Data *d; // needed for Objects and Arrays
+ Type t;
+};
+
+class JsonValueRef
+{
+public:
+ JsonValueRef(JsonArray *array, int idx)
+ : a(array), is_object(false), index(idx) {}
+ JsonValueRef(JsonObject *object, int idx)
+ : o(object), is_object(true), index(idx) {}
+
+ operator JsonValue() const { return toValue(); }
+ JsonValueRef &operator=(const JsonValue &val);
+ JsonValueRef &operator=(const JsonValueRef &val);
+
+ JsonValue::Type type() const { return toValue().type(); }
+ bool isNull() const { return type() == JsonValue::Null; }
+ bool isBool() const { return type() == JsonValue::Bool; }
+ bool isDouble() const { return type() == JsonValue::Double; }
+ bool isString() const { return type() == JsonValue::String; }
+ bool isArray() const { return type() == JsonValue::Array; }
+ bool isObject() const { return type() == JsonValue::Object; }
+ bool isUndefined() const { return type() == JsonValue::Undefined; }
+
+ std::string toString() const { return toValue().toString(); }
+ JsonArray toArray() const;
+ JsonObject toObject() const;
+
+ bool toBool(bool defaultValue = false) const { return toValue().toBool(defaultValue); }
+ int toInt(int defaultValue = 0) const { return toValue().toInt(defaultValue); }
+ double toDouble(double defaultValue = 0) const { return toValue().toDouble(defaultValue); }
+ std::string toString(const std::string &defaultValue) const { return toValue().toString(defaultValue); }
+
+ bool operator==(const JsonValue &other) const { return toValue() == other; }
+ bool operator!=(const JsonValue &other) const { return toValue() != other; }
+
+private:
+ JsonValue toValue() const;
+
+ union {
+ JsonArray *a;
+ JsonObject *o;
+ };
+ uint32_t is_object : 1;
+ uint32_t index : 31;
+};
+
+class JsonValuePtr
+{
+ JsonValue value;
+public:
+ explicit JsonValuePtr(const JsonValue& val)
+ : value(val) {}
+
+ JsonValue& operator*() { return value; }
+ JsonValue* operator->() { return &value; }
+};
+
+class JsonValueRefPtr
+{
+ JsonValueRef valueRef;
+public:
+ JsonValueRefPtr(JsonArray *array, int idx)
+ : valueRef(array, idx) {}
+ JsonValueRefPtr(JsonObject *object, int idx)
+ : valueRef(object, idx) {}
+
+ JsonValueRef& operator*() { return valueRef; }
+ JsonValueRef* operator->() { return &valueRef; }
+};
+
+
+
+class JsonArray
+{
+public:
+ JsonArray();
+ JsonArray(std::initializer_list<JsonValue> args);
+
+ ~JsonArray();
+
+ JsonArray(const JsonArray &other);
+ JsonArray &operator=(const JsonArray &other);
+
+ int size() const;
+ int count() const { return size(); }
+
+ bool isEmpty() const;
+ JsonValue at(int i) const;
+ JsonValue first() const;
+ JsonValue last() const;
+
+ void prepend(const JsonValue &value);
+ void append(const JsonValue &value);
+ void removeAt(int i);
+ JsonValue takeAt(int i);
+ void removeFirst() { removeAt(0); }
+ void removeLast() { removeAt(size() - 1); }
+
+ void insert(int i, const JsonValue &value);
+ void replace(int i, const JsonValue &value);
+
+ bool contains(const JsonValue &element) const;
+ JsonValueRef operator[](int i);
+ JsonValue operator[](int i) const;
+
+ bool operator==(const JsonArray &other) const;
+ bool operator!=(const JsonArray &other) const;
+
+ class const_iterator;
+
+ class iterator {
+ public:
+ JsonArray *a;
+ int i;
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef int difference_type;
+ typedef JsonValue value_type;
+ typedef JsonValueRef reference;
+ typedef JsonValueRefPtr pointer;
+
+ iterator() : a(nullptr), i(0) { }
+ explicit iterator(JsonArray *array, int index) : a(array), i(index) { }
+
+ JsonValueRef operator*() const { return JsonValueRef(a, i); }
+ JsonValueRefPtr operator->() const { return JsonValueRefPtr(a, i); }
+ JsonValueRef operator[](int j) const { return JsonValueRef(a, i + j); }
+
+ bool operator==(const iterator &o) const { return i == o.i; }
+ bool operator!=(const iterator &o) const { return i != o.i; }
+ bool operator<(const iterator& other) const { return i < other.i; }
+ bool operator<=(const iterator& other) const { return i <= other.i; }
+ bool operator>(const iterator& other) const { return i > other.i; }
+ bool operator>=(const iterator& other) const { return i >= other.i; }
+ bool operator==(const const_iterator &o) const { return i == o.i; }
+ bool operator!=(const const_iterator &o) const { return i != o.i; }
+ bool operator<(const const_iterator& other) const { return i < other.i; }
+ bool operator<=(const const_iterator& other) const { return i <= other.i; }
+ bool operator>(const const_iterator& other) const { return i > other.i; }
+ bool operator>=(const const_iterator& other) const { return i >= other.i; }
+ iterator &operator++() { ++i; return *this; }
+ iterator operator++(int) { iterator n = *this; ++i; return n; }
+ iterator &operator--() { i--; return *this; }
+ iterator operator--(int) { iterator n = *this; i--; return n; }
+ iterator &operator+=(int j) { i+=j; return *this; }
+ iterator &operator-=(int j) { i-=j; return *this; }
+ iterator operator+(int j) const { return iterator(a, i+j); }
+ iterator operator-(int j) const { return iterator(a, i-j); }
+ int operator-(iterator j) const { return i - j.i; }
+ };
+ friend class iterator;
+
+ class const_iterator {
+ public:
+ const JsonArray *a;
+ int i;
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+ typedef JsonValue value_type;
+ typedef JsonValue reference;
+ typedef JsonValuePtr pointer;
+
+ const_iterator() : a(nullptr), i(0) { }
+ explicit const_iterator(const JsonArray *array, int index) : a(array), i(index) { }
+ const_iterator(const iterator &o) : a(o.a), i(o.i) {}
+
+ JsonValue operator*() const { return a->at(i); }
+ JsonValuePtr operator->() const { return JsonValuePtr(a->at(i)); }
+ JsonValue operator[](int j) const { return a->at(i+j); }
+ bool operator==(const const_iterator &o) const { return i == o.i; }
+ bool operator!=(const const_iterator &o) const { return i != o.i; }
+ bool operator<(const const_iterator& other) const { return i < other.i; }
+ bool operator<=(const const_iterator& other) const { return i <= other.i; }
+ bool operator>(const const_iterator& other) const { return i > other.i; }
+ bool operator>=(const const_iterator& other) const { return i >= other.i; }
+ const_iterator &operator++() { ++i; return *this; }
+ const_iterator operator++(int) { const_iterator n = *this; ++i; return n; }
+ const_iterator &operator--() { i--; return *this; }
+ const_iterator operator--(int) { const_iterator n = *this; i--; return n; }
+ const_iterator &operator+=(int j) { i+=j; return *this; }
+ const_iterator &operator-=(int j) { i-=j; return *this; }
+ const_iterator operator+(int j) const { return const_iterator(a, i+j); }
+ const_iterator operator-(int j) const { return const_iterator(a, i-j); }
+ int operator-(const_iterator j) const { return i - j.i; }
+ };
+ friend class const_iterator;
+
+ // stl style
+ iterator begin() { detach(); return iterator(this, 0); }
+ const_iterator begin() const { return const_iterator(this, 0); }
+ const_iterator constBegin() const { return const_iterator(this, 0); }
+ iterator end() { detach(); return iterator(this, size()); }
+ const_iterator end() const { return const_iterator(this, size()); }
+ const_iterator constEnd() const { return const_iterator(this, size()); }
+ iterator insert(iterator before, const JsonValue &value) { insert(before.i, value); return before; }
+ iterator erase(iterator it) { removeAt(it.i); return it; }
+
+ void push_back(const JsonValue &t) { append(t); }
+ void push_front(const JsonValue &t) { prepend(t); }
+ void pop_front() { removeFirst(); }
+ void pop_back() { removeLast(); }
+ bool empty() const { return isEmpty(); }
+ typedef int size_type;
+ typedef JsonValue value_type;
+ typedef value_type *pointer;
+ typedef const value_type *const_pointer;
+ typedef JsonValueRef reference;
+ typedef JsonValue const_reference;
+ typedef int difference_type;
+
+private:
+ friend class Internal::Data;
+ friend class JsonValue;
+ friend class JsonDocument;
+
+ JsonArray(Internal::Data *data, Internal::Array *array);
+ void compact();
+ void detach(uint32_t reserve = 0);
+
+ Internal::Data *d;
+ Internal::Array *a;
+};
+
+
+class JsonObject
+{
+public:
+ JsonObject();
+ JsonObject(std::initializer_list<std::pair<std::string, JsonValue> > args);
+ ~JsonObject();
+
+ JsonObject(const JsonObject &other);
+ JsonObject &operator =(const JsonObject &other);
+
+ typedef std::vector<std::string> Keys;
+ Keys keys() const;
+ int size() const;
+ int count() const { return size(); }
+ int length() const { return size(); }
+ bool isEmpty() const;
+
+ JsonValue value(const std::string &key) const;
+ JsonValue operator[] (const std::string &key) const;
+ JsonValueRef operator[] (const std::string &key);
+
+ void remove(const std::string &key);
+ JsonValue take(const std::string &key);
+ bool contains(const std::string &key) const;
+
+ bool operator==(const JsonObject &other) const;
+ bool operator!=(const JsonObject &other) const;
+
+ class const_iterator;
+
+ class iterator
+ {
+ friend class const_iterator;
+ friend class JsonObject;
+ JsonObject *o;
+ int i;
+
+ public:
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef int difference_type;
+ typedef JsonValue value_type;
+ typedef JsonValueRef reference;
+
+ constexpr iterator() : o(nullptr), i(0) {}
+ constexpr iterator(JsonObject *obj, int index) : o(obj), i(index) {}
+
+ std::string key() const { return o->keyAt(i); }
+ JsonValueRef value() const { return JsonValueRef(o, i); }
+ JsonValueRef operator*() const { return JsonValueRef(o, i); }
+ JsonValueRefPtr operator->() const { return JsonValueRefPtr(o, i); }
+ bool operator==(const iterator &other) const { return i == other.i; }
+ bool operator!=(const iterator &other) const { return i != other.i; }
+
+ iterator &operator++() { ++i; return *this; }
+ iterator operator++(int) { iterator r = *this; ++i; return r; }
+ iterator &operator--() { --i; return *this; }
+ iterator operator--(int) { iterator r = *this; --i; return r; }
+ iterator operator+(int j) const
+ { iterator r = *this; r.i += j; return r; }
+ iterator operator-(int j) const { return operator+(-j); }
+ iterator &operator+=(int j) { i += j; return *this; }
+ iterator &operator-=(int j) { i -= j; return *this; }
+
+ public:
+ bool operator==(const const_iterator &other) const { return i == other.i; }
+ bool operator!=(const const_iterator &other) const { return i != other.i; }
+ };
+ friend class iterator;
+
+ class const_iterator
+ {
+ friend class iterator;
+ const JsonObject *o;
+ int i;
+
+ public:
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef int difference_type;
+ typedef JsonValue value_type;
+ typedef JsonValue reference;
+
+ constexpr const_iterator() : o(nullptr), i(0) {}
+ constexpr const_iterator(const JsonObject *obj, int index)
+ : o(obj), i(index) {}
+ const_iterator(const iterator &other)
+ : o(other.o), i(other.i) {}
+
+ std::string key() const { return o->keyAt(i); }
+ JsonValue value() const { return o->valueAt(i); }
+ JsonValue operator*() const { return o->valueAt(i); }
+ JsonValuePtr operator->() const { return JsonValuePtr(o->valueAt(i)); }
+ bool operator==(const const_iterator &other) const { return i == other.i; }
+ bool operator!=(const const_iterator &other) const { return i != other.i; }
+
+ const_iterator &operator++() { ++i; return *this; }
+ const_iterator operator++(int) { const_iterator r = *this; ++i; return r; }
+ const_iterator &operator--() { --i; return *this; }
+ const_iterator operator--(int) { const_iterator r = *this; --i; return r; }
+ const_iterator operator+(int j) const
+ { const_iterator r = *this; r.i += j; return r; }
+ const_iterator operator-(int j) const { return operator+(-j); }
+ const_iterator &operator+=(int j) { i += j; return *this; }
+ const_iterator &operator-=(int j) { i -= j; return *this; }
+
+ bool operator==(const iterator &other) const { return i == other.i; }
+ bool operator!=(const iterator &other) const { return i != other.i; }
+ };
+ friend class const_iterator;
+
+ // STL style
+ iterator begin() { detach(); return iterator(this, 0); }
+ const_iterator begin() const { return const_iterator(this, 0); }
+ const_iterator constBegin() const { return const_iterator(this, 0); }
+ iterator end() { detach(); return iterator(this, size()); }
+ const_iterator end() const { return const_iterator(this, size()); }
+ const_iterator constEnd() const { return const_iterator(this, size()); }
+ iterator erase(iterator it);
+
+ // more Qt
+ iterator find(const std::string &key);
+ const_iterator find(const std::string &key) const { return constFind(key); }
+ const_iterator constFind(const std::string &key) const;
+ iterator insert(const std::string &key, const JsonValue &value);
+
+ // STL compatibility
+ typedef JsonValue mapped_type;
+ typedef std::string key_type;
+ typedef int size_type;
+
+ bool empty() const { return isEmpty(); }
+
+private:
+ friend class Internal::Data;
+ friend class JsonValue;
+ friend class JsonDocument;
+ friend class JsonValueRef;
+
+ JsonObject(Internal::Data *data, Internal::Object *object);
+ void detach(uint32_t reserve = 0);
+ void compact();
+
+ std::string keyAt(int i) const;
+ JsonValue valueAt(int i) const;
+ void setValueAt(int i, const JsonValue &val);
+
+ Internal::Data *d;
+ Internal::Object *o;
+};
+
+struct JsonParseError
+{
+ enum ParseError {
+ NoError = 0,
+ UnterminatedObject,
+ MissingNameSeparator,
+ UnterminatedArray,
+ MissingValueSeparator,
+ IllegalValue,
+ TerminationByNumber,
+ IllegalNumber,
+ IllegalEscapeSequence,
+ IllegalUTF8String,
+ UnterminatedString,
+ MissingObject,
+ DeepNesting,
+ DocumentTooLarge,
+ GarbageAtEnd
+ };
+
+ int offset;
+ ParseError error;
+};
+
+class JsonDocument
+{
+public:
+ static const uint32_t BinaryFormatTag = ('q') | ('b' << 8) | ('j' << 16) | ('s' << 24);
+ JsonDocument();
+ explicit JsonDocument(const JsonObject &object);
+ explicit JsonDocument(const JsonArray &array);
+ ~JsonDocument();
+
+ JsonDocument(const JsonDocument &other);
+ JsonDocument &operator =(const JsonDocument &other);
+
+ enum DataValidation {
+ Validate,
+ BypassValidation
+ };
+
+ static JsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate);
+ const char *rawData(int *size) const;
+
+ static JsonDocument fromBinaryData(const std::string &data, DataValidation validation = Validate);
+ std::string toBinaryData() const;
+
+ enum JsonFormat {
+ Indented,
+ Compact
+ };
+
+ static JsonDocument fromJson(const std::string &json, JsonParseError *error = nullptr);
+
+ std::string toJson(JsonFormat format = Indented) const;
+
+ bool isEmpty() const;
+ bool isArray() const;
+ bool isObject() const;
+
+ JsonObject object() const;
+ JsonArray array() const;
+
+ void setObject(const JsonObject &object);
+ void setArray(const JsonArray &array);
+
+ bool operator==(const JsonDocument &other) const;
+ bool operator!=(const JsonDocument &other) const { return !(*this == other); }
+
+ bool isNull() const;
+
+private:
+ friend class JsonValue;
+ friend class Internal::Data;
+ friend class Internal::Parser;
+
+ JsonDocument(Internal::Data *data);
+
+ Internal::Data *d;
+};
+
+} // namespace Json
+
+#endif // JSONVALUE_H
diff --git a/src/shared/json/json.pri b/src/shared/json/json.pri
new file mode 100644
index 00000000000..db7ce19eee9
--- /dev/null
+++ b/src/shared/json/json.pri
@@ -0,0 +1,2 @@
+HEADERS += $$PWD/json.h
+SOURCES += $$PWD/json.cpp
diff --git a/src/shared/json/json.qbs b/src/shared/json/json.qbs
new file mode 100644
index 00000000000..3587d307afc
--- /dev/null
+++ b/src/shared/json/json.qbs
@@ -0,0 +1,15 @@
+import qbs
+
+StaticLibrary {
+ name: "qtcjson"
+ Depends { name: "cpp" }
+ cpp.cxxLanguageVersion: "c++11"
+ files: [
+ "json.cpp",
+ "json.h",
+ ]
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [product.sourceDirectory]
+ }
+}
diff --git a/src/src.qbs b/src/src.qbs
index 5b8c62e332e..1953849d02e 100644
--- a/src/src.qbs
+++ b/src/src.qbs
@@ -45,6 +45,7 @@ Project {
qbsBaseDir + "/src/plugins/plugins.qbs",
qbsBaseDir + "/share/share.qbs",
qbsBaseDir + "/src/app/apps.qbs",
+ project.sharedSourcesDir + "/json",
]
}
}
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 6284497e4c5..eabe10b3a19 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -13,6 +13,7 @@ SUBDIRS += \
profilewriter \
treeviewfind \
qtcprocess \
+ json \
utils \
filesearch \
sdktool \
diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs
index 91c6e593f88..15465a61e6f 100644
--- a/tests/auto/auto.qbs
+++ b/tests/auto/auto.qbs
@@ -14,6 +14,7 @@ Project {
"externaltool/externaltool.qbs",
"filesearch/filesearch.qbs",
"generichighlighter/generichighlighter.qbs",
+ "json/json.qbs",
"profilewriter/profilewriter.qbs",
"qml/qml.qbs",
"qtcprocess/qtcprocess.qbs",
diff --git a/tests/auto/json/bom.json b/tests/auto/json/bom.json
new file mode 100644
index 00000000000..d1e8d90e280
--- /dev/null
+++ b/tests/auto/json/bom.json
@@ -0,0 +1,3 @@
+{
+ "info-version": "1.0"
+}
diff --git a/tests/auto/json/json.pro b/tests/auto/json/json.pro
new file mode 100644
index 00000000000..b7c59706983
--- /dev/null
+++ b/tests/auto/json/json.pro
@@ -0,0 +1,13 @@
+TARGET = tst_json
+QT = core testlib
+CONFIG -= app_bundle
+CONFIG += testcase
+
+TESTDATA += test.json test.bjson test3.json test2.json bom.json
+
+INCLUDEPATH += ../../../src/shared/json
+
+DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
+
+include(../../../src/shared/json/json.pri)
+SOURCES += tst_json.cpp
diff --git a/tests/auto/json/json.qbs b/tests/auto/json/json.qbs
new file mode 100644
index 00000000000..de9a22fcf2a
--- /dev/null
+++ b/tests/auto/json/json.qbs
@@ -0,0 +1,22 @@
+import qbs
+
+QtcAutotest {
+ name: "json autotest"
+ Depends { name: "bundle" }
+ Depends { name: "qtcjson" }
+
+ bundle.isBundle: false
+
+ Group {
+ name: "test data"
+ files: [
+ "bom.json",
+ "test.bjson",
+ "test.json",
+ "test2.json",
+ "test3.json",
+ ]
+ }
+
+ files: ["tst_json.cpp"]
+}
diff --git a/tests/auto/json/test.bjson b/tests/auto/json/test.bjson
new file mode 100644
index 00000000000..ae12e5e446b
--- /dev/null
+++ b/tests/auto/json/test.bjson
Binary files differ
diff --git a/tests/auto/json/test.json b/tests/auto/json/test.json
new file mode 100644
index 00000000000..330756894a6
--- /dev/null
+++ b/tests/auto/json/test.json
@@ -0,0 +1,66 @@
+[
+ "JSON Test Pattern pass1",
+ {"object with 1 member":["array with 1 element"]},
+ {},
+ [],
+ -42,
+ true,
+ false,
+ null,
+ {
+ "integer": 1234567890,
+ "real": -9876.543210,
+ "e": 0.123456789e-12,
+ "E": 1.234567890E+34,
+ "": 23456789012E66,
+ "zero": 0,
+ "one": 1,
+ "space": " ",
+ "quote": "\"",
+ "backslash": "\\",
+ "controls": "\b\f\n\r\t",
+ "slash": "/ & \/",
+ "alpha": "abcdefghijklmnopqrstuvwxyz",
+ "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "digit": "0123456789",
+ "0123456789": "digit",
+ "special": "`1~!@#$%^&*()_+-={\':[,]}|;.</>?",
+ "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
+ "true": true,
+ "false": false,
+ "null": null,
+ "array":[ ],
+ "object":{ },
+ "address": "50 St. James Street",
+ "url": "nix",
+ "comment": "// /* <!-- --",
+ "# -- --> */": " ",
+ " s p a c e d " :[1,2 , 3
+
+,
+
+4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
+ "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
+ "quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
+ "\/\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?" : "A key can be any string"
+ },
+ 0.5 ,98.6
+,
+99.44
+,
+
+1066,
+1e1,
+0.1e1,
+1e-1,
+1e00,
+2e+00,
+2e-00,
+"rosebud",
+{"foo": "bar"},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}
+]
+
diff --git a/tests/auto/json/test2.json b/tests/auto/json/test2.json
new file mode 100644
index 00000000000..303f879b626
--- /dev/null
+++ b/tests/auto/json/test2.json
@@ -0,0 +1 @@
+{ "foo": ["ab"] }
diff --git a/tests/auto/json/test3.json b/tests/auto/json/test3.json
new file mode 100644
index 00000000000..48cb29a47f1
--- /dev/null
+++ b/tests/auto/json/test3.json
@@ -0,0 +1,15 @@
+{
+ "firstName": "John",
+ "lastName" : "Smith",
+ "age" : 25,
+ "address" : {
+ "streetAddress": "21 2nd Street",
+ "city" : "New York",
+ "state" : "NY",
+ "postalCode" : "10021"
+ },
+ "phoneNumber": [
+ { "type" : "home", "number": "212 555-1234" },
+ { "type" : "fax", "number": "646 555-4567" }
+ ]
+}
diff --git a/tests/auto/json/tst_json.cpp b/tests/auto/json/tst_json.cpp
new file mode 100644
index 00000000000..f24c09710de
--- /dev/null
+++ b/tests/auto/json/tst_json.cpp
@@ -0,0 +1,2524 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <json.h>
+
+#include <QTest>
+#include <QCryptographicHash>
+
+#include <limits>
+
+#define INVALID_UNICODE "\xCE\xBA\xE1"
+#define UNICODE_NON_CHARACTER "\xEF\xBF\xBF"
+#define UNICODE_DJE "\320\202" // Character from the Serbian Cyrillic alphabet
+
+using namespace Json;
+
+Q_DECLARE_METATYPE(Json::JsonArray)
+Q_DECLARE_METATYPE(Json::JsonObject)
+
+bool contains(const JsonObject::Keys &keys, const std::string &key)
+{
+ return std::find(keys.begin(), keys.end(), key) != keys.end();
+}
+
+class tst_Json: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_Json(QObject *parent = 0);
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void testValueSimple();
+ void testNumbers();
+ void testNumbers_2();
+ void testNumbers_3();
+
+ void testObjectSimple();
+ void testObjectSmallKeys();
+ void testArraySimple();
+ void testValueObject();
+ void testValueArray();
+ void testObjectNested();
+ void testArrayNested();
+ void testArrayNestedEmpty();
+ void testObjectNestedEmpty();
+
+ void testValueRef();
+ void testObjectIteration();
+ void testArrayIteration();
+
+ void testObjectFind();
+
+ void testDocument();
+
+ void nullValues();
+ void nullArrays();
+ void nullObject();
+ void constNullObject();
+
+ void keySorting();
+
+ void undefinedValues();
+
+ void toJson();
+ void toJsonSillyNumericValues();
+ void toJsonLargeNumericValues();
+ void fromJson();
+ void fromJsonErrors();
+ void fromBinary();
+ void toAndFromBinary_data();
+ void toAndFromBinary();
+ void parseNumbers();
+ void parseStrings();
+ void parseDuplicateKeys();
+ void testParser();
+
+ void compactArray();
+ void compactObject();
+
+ void validation();
+
+ void assignToDocument();
+
+ void testDuplicateKeys();
+ void testCompaction();
+ void testCompactionError();
+
+ void parseUnicodeEscapes();
+
+ void assignObjects();
+ void assignArrays();
+
+ void testTrailingComma();
+ void testDetachBug();
+ void testJsonValueRefDefault();
+
+ void valueEquals();
+ void objectEquals_data();
+ void objectEquals();
+ void arrayEquals_data();
+ void arrayEquals();
+
+ void bom();
+ void nesting();
+
+ void longStrings();
+
+ void arrayInitializerList();
+ void objectInitializerList();
+
+ void unicodeKeys();
+ void garbageAtEnd();
+
+ void removeNonLatinKey();
+private:
+ QString testDataDir;
+};
+
+tst_Json::tst_Json(QObject *parent) : QObject(parent)
+{
+}
+
+void tst_Json::initTestCase()
+{
+ testDataDir = QFileInfo(QFINDTESTDATA("test.json")).absolutePath();
+ if (testDataDir.isEmpty())
+ testDataDir = QCoreApplication::applicationDirPath();
+}
+
+void tst_Json::cleanupTestCase()
+{
+}
+
+void tst_Json::init()
+{
+}
+
+void tst_Json::cleanup()
+{
+}
+
+void tst_Json::testValueSimple()
+{
+ JsonObject object;
+ object.insert("number", 999.);
+ JsonArray array;
+ for (int i = 0; i < 10; ++i)
+ array.append((double)i);
+
+ JsonValue value(true);
+ QCOMPARE(value.type(), JsonValue::Bool);
+ QCOMPARE(value.toDouble(), 0.);
+ QCOMPARE(value.toString(), std::string());
+ QCOMPARE(value.toBool(), true);
+ QCOMPARE(value.toObject(), JsonObject());
+ QCOMPARE(value.toArray(), JsonArray());
+ QCOMPARE(value.toDouble(99.), 99.);
+ QCOMPARE(value.toString("test"), std::string("test"));
+ QCOMPARE(value.toObject(object), object);
+ QCOMPARE(value.toArray(array), array);
+
+ value = 999.;
+ QCOMPARE(value.type(), JsonValue::Double);
+ QCOMPARE(value.toDouble(), 999.);
+ QCOMPARE(value.toString(), std::string());
+ QCOMPARE(value.toBool(), false);
+ QCOMPARE(value.toBool(true), true);
+ QCOMPARE(value.toObject(), JsonObject());
+ QCOMPARE(value.toArray(), JsonArray());
+
+ value = "test";
+ QCOMPARE(value.toDouble(), 0.);
+ QCOMPARE(value.toString(), std::string("test"));
+ QCOMPARE(value.toBool(), false);
+ QCOMPARE(value.toObject(), JsonObject());
+ QCOMPARE(value.toArray(), JsonArray());
+}
+
+void tst_Json::testNumbers()
+{
+ {
+ int numbers[] = {
+ 0,
+ -1,
+ 1,
+ (1<<26),
+ (1<<27),
+ (1<<28),
+ -(1<<26),
+ -(1<<27),
+ -(1<<28),
+ (1<<26) - 1,
+ (1<<27) - 1,
+ (1<<28) - 1,
+ -((1<<26) - 1),
+ -((1<<27) - 1),
+ -((1<<28) - 1)
+ };
+ int n = sizeof(numbers)/sizeof(int);
+
+ JsonArray array;
+ for (int i = 0; i < n; ++i)
+ array.append((double)numbers[i]);
+
+ std::string serialized = JsonDocument(array).toJson();
+ JsonDocument json = JsonDocument::fromJson(serialized);
+ JsonArray array2 = json.array();
+
+ QCOMPARE(array.size(), array2.size());
+ for (int i = 0; i < array.size(); ++i) {
+ QCOMPARE(array.at(i).type(), JsonValue::Double);
+ QCOMPARE(array.at(i).toDouble(), (double)numbers[i]);
+ QCOMPARE(array2.at(i).type(), JsonValue::Double);
+ QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]);
+ }
+ }
+
+ {
+ int64_t numbers[] = {
+ 0,
+ -1,
+ 1,
+ (1ll<<54),
+ (1ll<<55),
+ (1ll<<56),
+ -(1ll<<54),
+ -(1ll<<55),
+ -(1ll<<56),
+ (1ll<<54) - 1,
+ (1ll<<55) - 1,
+ (1ll<<56) - 1,
+ -((1ll<<54) - 1),
+ -((1ll<<55) - 1),
+ -((1ll<<56) - 1)
+ };
+ int n = sizeof(numbers)/sizeof(int64_t);
+
+ JsonArray array;
+ for (int i = 0; i < n; ++i)
+ array.append((double)numbers[i]);
+
+ std::string serialized = JsonDocument(array).toJson();
+ JsonDocument json = JsonDocument::fromJson(serialized);
+ JsonArray array2 = json.array();
+
+ QCOMPARE(array.size(), array2.size());
+ for (int i = 0; i < array.size(); ++i) {
+ QCOMPARE(array.at(i).type(), JsonValue::Double);
+ QCOMPARE(array.at(i).toDouble(), (double)numbers[i]);
+ QCOMPARE(array2.at(i).type(), JsonValue::Double);
+ QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]);
+ }
+ }
+
+ {
+ double numbers[] = {
+ 0,
+ -1,
+ 1,
+ double(1ll<<54),
+ double(1ll<<55),
+ double(1ll<<56),
+ double(-(1ll<<54)),
+ double(-(1ll<<55)),
+ double(-(1ll<<56)),
+ double((1ll<<54) - 1),
+ double((1ll<<55) - 1),
+ double((1ll<<56) - 1),
+ double(-((1ll<<54) - 1)),
+ double(-((1ll<<55) - 1)),
+ double(-((1ll<<56) - 1)),
+ 1.1,
+ 0.1,
+ -0.1,
+ -1.1,
+ 1e200,
+ -1e200
+ };
+ int n = sizeof(numbers)/sizeof(double);
+
+ JsonArray array;
+ for (int i = 0; i < n; ++i)
+ array.append(numbers[i]);
+
+ std::string serialized = JsonDocument(array).toJson();
+ JsonDocument json = JsonDocument::fromJson(serialized);
+ JsonArray array2 = json.array();
+
+ QCOMPARE(array.size(), array2.size());
+ for (int i = 0; i < array.size(); ++i) {
+ QCOMPARE(array.at(i).type(), JsonValue::Double);
+ QCOMPARE(array.at(i).toDouble(), numbers[i]);
+ QCOMPARE(array2.at(i).type(), JsonValue::Double);
+ QCOMPARE(array2.at(i).toDouble(), numbers[i]);
+ }
+ }
+
+}
+
+void tst_Json::testNumbers_2()
+{
+ // test cases from TC39 test suite for ECMAScript
+ // http://hg.ecmascript.org/tests/test262/file/d067d2f0ca30/test/suite/ch08/8.5/8.5.1.js
+
+ // Fill an array with 2 to the power of (0 ... -1075)
+ double value = 1;
+ double floatValues[1076], floatValues_1[1076];
+ JsonObject jObject;
+ for (int power = 0; power <= 1075; power++) {
+ floatValues[power] = value;
+ jObject.insert(std::to_string(power), JsonValue(floatValues[power]));
+ // Use basic math operations for testing, which are required to support 'gradual underflow' rather
+ // than Math.pow etc..., which are defined as 'implementation dependent'.
+ value = value * 0.5;
+ }
+
+ JsonDocument jDocument1(jObject);
+ std::string ba(jDocument1.toJson());
+
+ JsonDocument jDocument2(JsonDocument::fromJson(ba));
+ for (int power = 0; power <= 1075; power++) {
+ floatValues_1[power] = jDocument2.object().value(std::to_string(power)).toDouble();
+#ifdef Q_OS_QNX
+ if (power >= 970)
+ QEXPECT_FAIL("", "See QTBUG-37066", Abort);
+#endif
+ QVERIFY2(floatValues[power] == floatValues_1[power],
+ QString::fromLatin1("floatValues[%1] != floatValues_1[%1]").arg(power).toLatin1());
+ }
+
+ // The last value is below min denorm and should round to 0, everything else should contain a value
+ QVERIFY2(floatValues_1[1075] == 0, "Value after min denorm should round to 0");
+
+ // Validate the last actual value is min denorm
+ QVERIFY2(floatValues_1[1074] == 4.9406564584124654417656879286822e-324,
+ QString::fromLatin1("Min denorm value is incorrect: %1").arg(floatValues_1[1074]).toLatin1());
+
+ // Validate that every value is half the value before it up to 1
+ for (int index = 1074; index > 0; index--) {
+ QVERIFY2(floatValues_1[index] != 0,
+ QString::fromLatin1("2**- %1 should not be 0").arg(index).toLatin1());
+
+ QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2),
+ QString::fromLatin1("Value should be double adjacent value at index %1").arg(index).toLatin1());
+ }
+}
+
+void tst_Json::testNumbers_3()
+{
+ // test case from QTBUG-31926
+ double d1 = 1.123451234512345;
+ double d2 = 1.123451234512346;
+
+ JsonObject jObject;
+ jObject.insert("d1", JsonValue(d1));
+ jObject.insert("d2", JsonValue(d2));
+ JsonDocument jDocument1(jObject);
+ std::string ba(jDocument1.toJson());
+
+ JsonDocument jDocument2(JsonDocument::fromJson(ba));
+
+ double d1_1(jDocument2.object().value("d1").toDouble());
+ double d2_1(jDocument2.object().value("d2").toDouble());
+ QVERIFY(d1_1 != d2_1);
+}
+
+void tst_Json::testObjectSimple()
+{
+ JsonObject object;
+ object.insert("number", 999.);
+ QCOMPARE(object.value("number").type(), JsonValue::Double);
+ QCOMPARE(object.value("number").toDouble(), 999.);
+ object.insert("string", std::string("test"));
+ QCOMPARE(object.value("string").type(), JsonValue::String);
+ QCOMPARE(object.value("string").toString(), std::string("test"));
+ object.insert("boolean", true);
+ QCOMPARE(object.value("boolean").toBool(), true);
+
+ JsonObject::Keys keys = object.keys();
+ QVERIFY2(contains(keys, "number"), "key number not found");
+ QVERIFY2(contains(keys, "string"), "key string not found");
+ QVERIFY2(contains(keys, "boolean"), "key boolean not found");
+
+ // if we put a JsonValue into the JsonObject and retrieve
+ // it, it should be identical.
+ JsonValue value("foo");
+ object.insert("value", value);
+ QCOMPARE(object.value("value"), value);
+
+ int size = object.size();
+ object.remove("boolean");
+ QCOMPARE(object.size(), size - 1);
+ QVERIFY2(!object.contains("boolean"), "key boolean should have been removed");
+
+ JsonValue taken = object.take("value");
+ QCOMPARE(taken, value);
+ QVERIFY2(!object.contains("value"), "key value should have been removed");
+
+ std::string before = object.value("string").toString();
+ object.insert("string", std::string("foo"));
+ QVERIFY2(object.value("string").toString() != before, "value should have been updated");
+
+ size = object.size();
+ JsonObject subobject;
+ subobject.insert("number", 42);
+ subobject.insert("string", "foobar");
+ object.insert("subobject", subobject);
+ QCOMPARE(object.size(), size+1);
+ JsonValue subvalue = object.take("subobject");
+ QCOMPARE(object.size(), size);
+ QCOMPARE(subvalue.toObject(), subobject);
+ // make object detach by modifying it many times
+ for (int i = 0; i < 64; ++i)
+ object.insert("string", "bar");
+ QCOMPARE(object.size(), size);
+ QCOMPARE(subvalue.toObject(), subobject);
+}
+
+void tst_Json::testObjectSmallKeys()
+{
+ JsonObject data1;
+ data1.insert("1", 123.);
+ QVERIFY(data1.contains("1"));
+ QCOMPARE(data1.value("1").toDouble(), (double)123);
+ data1.insert("12", 133.);
+ QCOMPARE(data1.value("12").toDouble(), (double)133);
+ QVERIFY(data1.contains("12"));
+ data1.insert("123", 323.);
+ QCOMPARE(data1.value("12").toDouble(), (double)133);
+ QVERIFY(data1.contains("123"));
+ QCOMPARE(data1.value("123").type(), JsonValue::Double);
+ QCOMPARE(data1.value("123").toDouble(), (double)323);
+}
+
+void tst_Json::testArraySimple()
+{
+ JsonArray array;
+ array.append(999.);
+ array.append(std::string("test"));
+ array.append(true);
+
+ JsonValue val = array.at(0);
+ QCOMPARE(array.at(0).toDouble(), 999.);
+ QCOMPARE(array.at(1).toString(), std::string("test"));
+ QCOMPARE(array.at(2).toBool(), true);
+ QCOMPARE(array.size(), 3);
+
+ // if we put a JsonValue into the JsonArray and retrieve
+ // it, it should be identical.
+ JsonValue value("foo");
+ array.append(value);
+ QCOMPARE(array.at(3), value);
+
+ int size = array.size();
+ array.removeAt(2);
+ --size;
+ QCOMPARE(array.size(), size);
+
+ JsonValue taken = array.takeAt(0);
+ --size;
+ QCOMPARE(taken.toDouble(), 999.);
+ QCOMPARE(array.size(), size);
+
+ // check whether null values work
+ array.append(JsonValue());
+ ++size;
+ QCOMPARE(array.size(), size);
+ QCOMPARE(array.last().type(), JsonValue::Null);
+ QCOMPARE(array.last(), JsonValue());
+
+ QCOMPARE(array.first().type(), JsonValue::String);
+ QCOMPARE(array.first(), JsonValue("test"));
+
+ array.prepend(false);
+ QCOMPARE(array.first().type(), JsonValue::Bool);
+ QCOMPARE(array.first(), JsonValue(false));
+
+ QCOMPARE(array.at(-1), JsonValue(JsonValue::Undefined));
+ QCOMPARE(array.at(array.size()), JsonValue(JsonValue::Undefined));
+
+ array.replace(0, -555.);
+ QCOMPARE(array.first().type(), JsonValue::Double);
+ QCOMPARE(array.first(), JsonValue(-555.));
+ QCOMPARE(array.at(1).type(), JsonValue::String);
+ QCOMPARE(array.at(1), JsonValue("test"));
+}
+
+void tst_Json::testValueObject()
+{
+ JsonObject object;
+ object.insert("number", 999.);
+ object.insert("string", "test");
+ object.insert("boolean", true);
+
+ JsonValue value(object);
+
+ // if we don't modify the original JsonObject, toObject()
+ // on the JsonValue should return the same object (non-detached).
+ QCOMPARE(value.toObject(), object);
+
+ // if we modify the original object, it should detach
+ object.insert("test", JsonValue("test"));
+ QVERIFY2(value.toObject() != object, "object should have detached");
+}
+
+void tst_Json::testValueArray()
+{
+ JsonArray array;
+ array.append(999.);
+ array.append("test");
+ array.append(true);
+
+ JsonValue value(array);
+
+ // if we don't modify the original JsonArray, toArray()
+ // on the JsonValue should return the same object (non-detached).
+ QCOMPARE(value.toArray(), array);
+
+ // if we modify the original array, it should detach
+ array.append("test");
+ QVERIFY2(value.toArray() != array, "array should have detached");
+}
+
+void tst_Json::testObjectNested()
+{
+ JsonObject inner, outer;
+ inner.insert("number", 999.);
+ outer.insert("nested", inner);
+
+ // if we don't modify the original JsonObject, value()
+ // should return the same object (non-detached).
+ JsonObject value = outer.value("nested").toObject();
+ QCOMPARE(value, inner);
+ QCOMPARE(value.value("number").toDouble(), 999.);
+
+ // if we modify the original object, it should detach and not
+ // affect the nested object
+ inner.insert("number", 555.);
+ value = outer.value("nested").toObject();
+ QVERIFY2(inner.value("number").toDouble() != value.value("number").toDouble(),
+ "object should have detached");
+
+ // array in object
+ JsonArray array;
+ array.append(123.);
+ array.append(456.);
+ outer.insert("array", array);
+ QCOMPARE(outer.value("array").toArray(), array);
+ QCOMPARE(outer.value("array").toArray().at(1).toDouble(), 456.);
+
+ // two deep objects
+ JsonObject twoDeep;
+ twoDeep.insert("boolean", true);
+ inner.insert("nested", twoDeep);
+ outer.insert("nested", inner);
+ QCOMPARE(outer.value("nested").toObject().value("nested").toObject(), twoDeep);
+ QCOMPARE(outer.value("nested").toObject().value("nested").toObject().value("boolean").toBool(),
+ true);
+}
+
+void tst_Json::testArrayNested()
+{
+ JsonArray inner, outer;
+ inner.append(999.);
+ outer.append(inner);
+
+ // if we don't modify the original JsonArray, value()
+ // should return the same array (non-detached).
+ JsonArray value = outer.at(0).toArray();
+ QCOMPARE(value, inner);
+ QCOMPARE(value.at(0).toDouble(), 999.);
+
+ // if we modify the original array, it should detach and not
+ // affect the nested array
+ inner.append(555.);
+ value = outer.at(0).toArray();
+ QVERIFY2(inner.size() != value.size(), "array should have detached");
+
+ // objects in arrays
+ JsonObject object;
+ object.insert("boolean", true);
+ outer.append(object);
+ QCOMPARE(outer.last().toObject(), object);
+ QCOMPARE(outer.last().toObject().value("boolean").toBool(), true);
+
+ // two deep arrays
+ JsonArray twoDeep;
+ twoDeep.append(JsonValue("nested"));
+ inner.append(twoDeep);
+ outer.append(inner);
+ QCOMPARE(outer.last().toArray().last().toArray(), twoDeep);
+ QCOMPARE(outer.last().toArray().last().toArray().at(0).toString(), std::string("nested"));
+}
+
+void tst_Json::testArrayNestedEmpty()
+{
+ JsonObject object;
+ JsonArray inner;
+ object.insert("inner", inner);
+ JsonValue val = object.value("inner");
+ JsonArray value = object.value("inner").toArray();
+ QCOMPARE(value.size(), 0);
+ QCOMPARE(value, inner);
+ QCOMPARE(value.size(), 0);
+ object.insert("count", 0.);
+ QCOMPARE(object.value("inner").toArray().size(), 0);
+ QVERIFY(object.value("inner").toArray().isEmpty());
+ JsonDocument(object).toBinaryData();
+ QCOMPARE(object.value("inner").toArray().size(), 0);
+}
+
+void tst_Json::testObjectNestedEmpty()
+{
+ JsonObject object;
+ JsonObject inner;
+ JsonObject inner2;
+ object.insert("inner", inner);
+ object.insert("inner2", inner2);
+ JsonObject value = object.value("inner").toObject();
+ QCOMPARE(value.size(), 0);
+ QCOMPARE(value, inner);
+ QCOMPARE(value.size(), 0);
+ object.insert("count", 0.);
+ QCOMPARE(object.value("inner").toObject().size(), 0);
+ QCOMPARE(object.value("inner").type(), JsonValue::Object);
+ JsonDocument(object).toBinaryData();
+ QVERIFY(object.value("inner").toObject().isEmpty());
+ QVERIFY(object.value("inner2").toObject().isEmpty());
+ JsonDocument doc = JsonDocument::fromBinaryData(JsonDocument(object).toBinaryData());
+ QVERIFY(!doc.isNull());
+ JsonObject reconstituted(doc.object());
+ QCOMPARE(reconstituted.value("inner").toObject().size(), 0);
+ QCOMPARE(reconstituted.value("inner").type(), JsonValue::Object);
+ QCOMPARE(reconstituted.value("inner2").type(), JsonValue::Object);
+}
+
+void tst_Json::testValueRef()
+{
+ JsonArray array;
+ array.append(1.);
+ array.append(2.);
+ array.append(3.);
+ array.append(4);
+ array.append(4.1);
+ array[1] = false;
+
+ QCOMPARE(array.size(), 5);
+ QCOMPARE(array.at(0).toDouble(), 1.);
+ QCOMPARE(array.at(2).toDouble(), 3.);
+ QCOMPARE(array.at(3).toInt(), 4);
+ QCOMPARE(array.at(4).toInt(), 0);
+ QCOMPARE(array.at(1).type(), JsonValue::Bool);
+ QCOMPARE(array.at(1).toBool(), false);
+
+ JsonObject object;
+ object["key"] = true;
+ QCOMPARE(object.size(), 1);
+ object.insert("null", JsonValue());
+ QCOMPARE(object.value("null"), JsonValue());
+ object["null"] = 100.;
+ QCOMPARE(object.value("null").type(), JsonValue::Double);
+ JsonValue val = object["null"];
+ QCOMPARE(val.toDouble(), 100.);
+ QCOMPARE(object.size(), 2);
+
+ array[1] = array[2] = object["key"] = 42;
+ QCOMPARE(array[1], array[2]);
+ QCOMPARE(array[2], object["key"]);
+ QCOMPARE(object.value("key"), JsonValue(42));
+}
+
+void tst_Json::testObjectIteration()
+{
+ JsonObject object;
+
+ for (JsonObject::iterator it = object.begin(); it != object.end(); ++it)
+ QVERIFY(false);
+
+ const std::string property = "kkk";
+ object.insert(property, 11);
+ object.take(property);
+ for (JsonObject::iterator it = object.begin(); it != object.end(); ++it)
+ QVERIFY(false);
+
+ for (int i = 0; i < 10; ++i)
+ object[std::to_string(i)] = (double)i;
+
+ QCOMPARE(object.size(), 10);
+
+ QCOMPARE(object.begin()->toDouble(), object.constBegin()->toDouble());
+
+ for (JsonObject::iterator it = object.begin(); it != object.end(); ++it) {
+ JsonValue value = it.value();
+ QCOMPARE((double)atoi(it.key().data()), value.toDouble());
+ }
+
+ {
+ JsonObject object2 = object;
+ QCOMPARE(object, object2);
+
+ JsonValue val = *object2.begin();
+ object2.erase(object2.begin());
+ QCOMPARE(object.size(), 10);
+ QCOMPARE(object2.size(), 9);
+
+ for (JsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) {
+ JsonValue value = it.value();
+ QVERIFY(it.value() != val);
+ QCOMPARE((double)atoi(it.key().data()), value.toDouble());
+ }
+ }
+
+ {
+ JsonObject object2 = object;
+ QCOMPARE(object, object2);
+
+ JsonObject::iterator it = object2.find(std::to_string(5));
+ object2.erase(it);
+ QCOMPARE(object.size(), 10);
+ QCOMPARE(object2.size(), 9);
+ }
+
+ {
+ JsonObject::iterator it = object.begin();
+ it += 5;
+ QCOMPARE(JsonValue(it.value()).toDouble(), 5.);
+ it -= 3;
+ QCOMPARE(JsonValue(it.value()).toDouble(), 2.);
+ JsonObject::iterator it2 = it + 5;
+ QCOMPARE(JsonValue(it2.value()).toDouble(), 7.);
+ it2 = it - 1;
+ QCOMPARE(JsonValue(it2.value()).toDouble(), 1.);
+ }
+
+ {
+ JsonObject::const_iterator it = object.constBegin();
+ it += 5;
+ QCOMPARE(JsonValue(it.value()).toDouble(), 5.);
+ it -= 3;
+ QCOMPARE(JsonValue(it.value()).toDouble(), 2.);
+ JsonObject::const_iterator it2 = it + 5;
+ QCOMPARE(JsonValue(it2.value()).toDouble(), 7.);
+ it2 = it - 1;
+ QCOMPARE(JsonValue(it2.value()).toDouble(), 1.);
+ }
+
+ JsonObject::iterator it = object.begin();
+ while (!object.isEmpty())
+ it = object.erase(it);
+ QCOMPARE(object.size() , 0);
+ QCOMPARE(it, object.end());
+}
+
+void tst_Json::testArrayIteration()
+{
+ JsonArray array;
+ for (int i = 0; i < 10; ++i)
+ array.append(i);
+
+ QCOMPARE(array.size(), 10);
+
+ int i = 0;
+ for (JsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) {
+ JsonValue value = (*it);
+ QCOMPARE((double)i, value.toDouble());
+ }
+
+ QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble());
+
+ {
+ JsonArray array2 = array;
+ QCOMPARE(array, array2);
+
+ JsonValue val = *array2.begin();
+ array2.erase(array2.begin());
+ QCOMPARE(array.size(), 10);
+ QCOMPARE(array2.size(), 9);
+
+ i = 1;
+ for (JsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) {
+ JsonValue value = (*it);
+ QCOMPARE((double)i, value.toDouble());
+ }
+ }
+
+ {
+ JsonArray::iterator it = array.begin();
+ it += 5;
+ QCOMPARE(JsonValue((*it)).toDouble(), 5.);
+ it -= 3;
+ QCOMPARE(JsonValue((*it)).toDouble(), 2.);
+ JsonArray::iterator it2 = it + 5;
+ QCOMPARE(JsonValue(*it2).toDouble(), 7.);
+ it2 = it - 1;
+ QCOMPARE(JsonValue(*it2).toDouble(), 1.);
+ }
+
+ {
+ JsonArray::const_iterator it = array.constBegin();
+ it += 5;
+ QCOMPARE(JsonValue((*it)).toDouble(), 5.);
+ it -= 3;
+ QCOMPARE(JsonValue((*it)).toDouble(), 2.);
+ JsonArray::const_iterator it2 = it + 5;
+ QCOMPARE(JsonValue(*it2).toDouble(), 7.);
+ it2 = it - 1;
+ QCOMPARE(JsonValue(*it2).toDouble(), 1.);
+ }
+
+ JsonArray::iterator it = array.begin();
+ while (!array.isEmpty())
+ it = array.erase(it);
+ QCOMPARE(array.size() , 0);
+ QCOMPARE(it, array.end());
+}
+
+void tst_Json::testObjectFind()
+{
+ JsonObject object;
+ for (int i = 0; i < 10; ++i)
+ object[std::to_string(i)] = i;
+
+ QCOMPARE(object.size(), 10);
+
+ JsonObject::iterator it = object.find("1");
+ QCOMPARE((*it).toDouble(), 1.);
+ it = object.find("11");
+ QCOMPARE((*it).type(), JsonValue::Undefined);
+ QCOMPARE(it, object.end());
+
+ JsonObject::const_iterator cit = object.constFind("1");
+ QCOMPARE((*cit).toDouble(), 1.);
+ cit = object.constFind("11");
+ QCOMPARE((*it).type(), JsonValue::Undefined);
+ QCOMPARE(it, object.end());
+}
+
+void tst_Json::testDocument()
+{
+ JsonDocument doc;
+ QCOMPARE(doc.isEmpty(), true);
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), false);
+
+ JsonObject object;
+ doc.setObject(object);
+ QCOMPARE(doc.isEmpty(), false);
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), true);
+
+ object.insert("Key", "Value");
+ doc.setObject(object);
+ QCOMPARE(doc.isEmpty(), false);
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), true);
+ QCOMPARE(doc.object(), object);
+ QCOMPARE(doc.array(), JsonArray());
+
+ doc = JsonDocument();
+ QCOMPARE(doc.isEmpty(), true);
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), false);
+
+ JsonArray array;
+ doc.setArray(array);
+ QCOMPARE(doc.isEmpty(), false);
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+
+ array.append("Value");
+ doc.setArray(array);
+ QCOMPARE(doc.isEmpty(), false);
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ QCOMPARE(doc.array(), array);
+ QCOMPARE(doc.object(), JsonObject());
+
+ JsonObject outer;
+ outer.insert("outerKey", 22);
+ JsonObject inner;
+ inner.insert("innerKey", 42);
+ outer.insert("innter", inner);
+ JsonArray innerArray;
+ innerArray.append(23);
+ outer.insert("innterArray", innerArray);
+
+ JsonDocument doc2(outer.value("innter").toObject());
+ QVERIFY(doc2.object().contains("innerKey"));
+ QCOMPARE(doc2.object().value("innerKey"), JsonValue(42));
+
+ JsonDocument doc3;
+ doc3.setObject(outer.value("innter").toObject());
+ QCOMPARE(doc3.isArray(), false);
+ QCOMPARE(doc3.isObject(), true);
+ QVERIFY(doc3.object().contains("innerKey"));
+ QCOMPARE(doc3.object().value("innerKey"), JsonValue(42));
+
+ JsonDocument doc4(outer.value("innterArray").toArray());
+ QCOMPARE(doc4.isArray(), true);
+ QCOMPARE(doc4.isObject(), false);
+ QCOMPARE(doc4.array().size(), 1);
+ QCOMPARE(doc4.array().at(0), JsonValue(23));
+
+ JsonDocument doc5;
+ doc5.setArray(outer.value("innterArray").toArray());
+ QCOMPARE(doc5.isArray(), true);
+ QCOMPARE(doc5.isObject(), false);
+ QCOMPARE(doc5.array().size(), 1);
+ QCOMPARE(doc5.array().at(0), JsonValue(23));
+}
+
+void tst_Json::nullValues()
+{
+ JsonArray array;
+ array.append(JsonValue());
+
+ QCOMPARE(array.size(), 1);
+ QCOMPARE(array.at(0), JsonValue());
+
+ JsonObject object;
+ object.insert("key", JsonValue());
+ QCOMPARE(object.contains("key"), true);
+ QCOMPARE(object.size(), 1);
+ QCOMPARE(object.value("key"), JsonValue());
+}
+
+void tst_Json::nullArrays()
+{
+ JsonArray nullArray;
+ JsonArray nonNull;
+ nonNull.append("bar");
+
+ QCOMPARE(nullArray, JsonArray());
+ QVERIFY(nullArray != nonNull);
+ QVERIFY(nonNull != nullArray);
+
+ QCOMPARE(nullArray.size(), 0);
+ QCOMPARE(nullArray.takeAt(0), JsonValue(JsonValue::Undefined));
+ QCOMPARE(nullArray.first(), JsonValue(JsonValue::Undefined));
+ QCOMPARE(nullArray.last(), JsonValue(JsonValue::Undefined));
+ nullArray.removeAt(0);
+ nullArray.removeAt(-1);
+
+ nullArray.append("bar");
+ nullArray.removeAt(0);
+
+ QCOMPARE(nullArray.size(), 0);
+ QCOMPARE(nullArray.takeAt(0), JsonValue(JsonValue::Undefined));
+ QCOMPARE(nullArray.first(), JsonValue(JsonValue::Undefined));
+ QCOMPARE(nullArray.last(), JsonValue(JsonValue::Undefined));
+ nullArray.removeAt(0);
+ nullArray.removeAt(-1);
+}
+
+void tst_Json::nullObject()
+{
+ JsonObject nullObject;
+ JsonObject nonNull;
+ nonNull.insert("foo", "bar");
+
+ QCOMPARE(nullObject, JsonObject());
+ QVERIFY(nullObject != nonNull);
+ QVERIFY(nonNull != nullObject);
+
+ QCOMPARE(nullObject.size(), 0);
+ QCOMPARE(nullObject.keys(), JsonObject::Keys());
+ nullObject.remove("foo");
+ QCOMPARE(nullObject, JsonObject());
+ QCOMPARE(nullObject.take("foo"), JsonValue(JsonValue::Undefined));
+ QCOMPARE(nullObject.contains("foo"), false);
+
+ nullObject.insert("foo", "bar");
+ nullObject.remove("foo");
+
+ QCOMPARE(nullObject.size(), 0);
+ QCOMPARE(nullObject.keys(), JsonObject::Keys());
+ nullObject.remove("foo");
+ QCOMPARE(nullObject, JsonObject());
+ QCOMPARE(nullObject.take("foo"), JsonValue(JsonValue::Undefined));
+ QCOMPARE(nullObject.contains("foo"), false);
+}
+
+void tst_Json::constNullObject()
+{
+ const JsonObject nullObject;
+ JsonObject nonNull;
+ nonNull.insert("foo", "bar");
+
+ QCOMPARE(nullObject, JsonObject());
+ QVERIFY(nullObject != nonNull);
+ QVERIFY(nonNull != nullObject);
+
+ QCOMPARE(nullObject.size(), 0);
+ QCOMPARE(nullObject.keys(), JsonObject::Keys());
+ QCOMPARE(nullObject, JsonObject());
+ QCOMPARE(nullObject.contains("foo"), false);
+ QCOMPARE(nullObject["foo"], JsonValue(JsonValue::Undefined));
+}
+
+void tst_Json::keySorting()
+{
+ const char *json = "{ \"B\": true, \"A\": false }";
+ JsonDocument doc = JsonDocument::fromJson(json);
+
+ QCOMPARE(doc.isObject(), true);
+
+ JsonObject o = doc.object();
+ QCOMPARE(o.size(), 2);
+ JsonObject::const_iterator it = o.constBegin();
+ QCOMPARE(it.key(), std::string("A"));
+ ++it;
+ QCOMPARE(it.key(), std::string("B"));
+
+ JsonObject::Keys keys;
+ keys.push_back("A");
+ keys.push_back("B");
+ QCOMPARE(o.keys(), keys);
+}
+
+void tst_Json::undefinedValues()
+{
+ JsonObject object;
+ object.insert("Key", JsonValue(JsonValue::Undefined));
+ QCOMPARE(object.size(), 0);
+
+ object.insert("Key", "Value");
+ QCOMPARE(object.size(), 1);
+ QCOMPARE(object.value("Key").type(), JsonValue::String);
+ QCOMPARE(object.value("foo").type(), JsonValue::Undefined);
+ object.insert("Key", JsonValue(JsonValue::Undefined));
+ QCOMPARE(object.size(), 0);
+ QCOMPARE(object.value("Key").type(), JsonValue::Undefined);
+
+ JsonArray array;
+ array.append(JsonValue(JsonValue::Undefined));
+ QCOMPARE(array.size(), 1);
+ QCOMPARE(array.at(0).type(), JsonValue::Null);
+
+ QCOMPARE(array.at(1).type(), JsonValue::Undefined);
+ QCOMPARE(array.at(-1).type(), JsonValue::Undefined);
+}
+
+void tst_Json::toJson()
+{
+ // Test JsonDocument::Indented format
+ {
+ JsonObject object;
+ object.insert("\\Key\n", "Value");
+ object.insert("null", JsonValue());
+ JsonArray array;
+ array.append(true);
+ array.append(999.);
+ array.append("string");
+ array.append(JsonValue());
+ array.append("\\\a\n\r\b\tabcABC\"");
+ object.insert("Array", array);
+
+ std::string json = JsonDocument(object).toJson();
+
+ std::string expected =
+ "{\n"
+ " \"Array\": [\n"
+ " true,\n"
+ " 999,\n"
+ " \"string\",\n"
+ " null,\n"
+ " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n"
+ " ],\n"
+ " \"\\\\Key\\n\": \"Value\",\n"
+ " \"null\": null\n"
+ "}\n";
+ QCOMPARE(json, expected);
+
+ JsonDocument doc;
+ doc.setObject(object);
+ json = doc.toJson();
+ QCOMPARE(json, expected);
+
+ doc.setArray(array);
+ json = doc.toJson();
+ expected =
+ "[\n"
+ " true,\n"
+ " 999,\n"
+ " \"string\",\n"
+ " null,\n"
+ " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n"
+ "]\n";
+ QCOMPARE(json, expected);
+ }
+
+ // Test JsonDocument::Compact format
+ {
+ JsonObject object;
+ object.insert("\\Key\n", "Value");
+ object.insert("null", JsonValue());
+ JsonArray array;
+ array.append(true);
+ array.append(999.);
+ array.append("string");
+ array.append(JsonValue());
+ array.append("\\\a\n\r\b\tabcABC\"");
+ object.insert("Array", array);
+
+ std::string json = JsonDocument(object).toJson(JsonDocument::Compact);
+ std::string expected =
+ "{\"Array\":[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\":\"Value\",\"null\":null}";
+ QCOMPARE(json, expected);
+
+ JsonDocument doc;
+ doc.setObject(object);
+ json = doc.toJson(JsonDocument::Compact);
+ QCOMPARE(json, expected);
+
+ doc.setArray(array);
+ json = doc.toJson(JsonDocument::Compact);
+ expected = "[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"]";
+ QCOMPARE(json, expected);
+ }
+}
+
+void tst_Json::toJsonSillyNumericValues()
+{
+ JsonObject object;
+ JsonArray array;
+ array.append(JsonValue(std::numeric_limits<double>::infinity())); // encode to: null
+ array.append(JsonValue(-std::numeric_limits<double>::infinity())); // encode to: null
+ array.append(JsonValue(std::numeric_limits<double>::quiet_NaN())); // encode to: null
+ object.insert("Array", array);
+
+ std::string json = JsonDocument(object).toJson();
+
+ std::string expected =
+ "{\n"
+ " \"Array\": [\n"
+ " null,\n"
+ " null,\n"
+ " null\n"
+ " ]\n"
+ "}\n";
+
+ QCOMPARE(json, expected);
+
+ JsonDocument doc;
+ doc.setObject(object);
+ json = doc.toJson();
+ QCOMPARE(json, expected);
+}
+
+void tst_Json::toJsonLargeNumericValues()
+{
+ JsonObject object;
+ JsonArray array;
+ array.append(JsonValue(1.234567)); // actual precision bug in Qt 5.0.0
+ array.append(JsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE
+ array.append(JsonValue(5e-324)); // JS Number.MIN_VALUE
+ array.append(JsonValue(std::numeric_limits<double>::min()));
+ array.append(JsonValue(std::numeric_limits<double>::max()));
+ array.append(JsonValue(std::numeric_limits<double>::epsilon()));
+ array.append(JsonValue(std::numeric_limits<double>::denorm_min()));
+ array.append(JsonValue(0.0));
+ array.append(JsonValue(-std::numeric_limits<double>::min()));
+ array.append(JsonValue(-std::numeric_limits<double>::max()));
+ array.append(JsonValue(-std::numeric_limits<double>::epsilon()));
+ array.append(JsonValue(-std::numeric_limits<double>::denorm_min()));
+ array.append(JsonValue(-0.0));
+ array.append(JsonValue(int64_t(9007199254740992LL))); // JS Number max integer
+ array.append(JsonValue(int64_t(-9007199254740992LL))); // JS Number min integer
+ object.insert("Array", array);
+
+ std::string json = JsonDocument(object).toJson();
+
+ std::string expected =
+ "{\n"
+ " \"Array\": [\n"
+ " 1.234567,\n"
+ " 1.7976931348623157e+308,\n"
+ // ((4.9406564584124654e-324 == 5e-324) == true)
+ // I can only think JavaScript has a special formatter to
+ // emit this value for this IEEE754 bit pattern.
+ " 4.9406564584124654e-324,\n"
+ " 2.2250738585072014e-308,\n"
+ " 1.7976931348623157e+308,\n"
+ " 2.2204460492503131e-16,\n"
+ " 4.9406564584124654e-324,\n"
+ " 0,\n"
+ " -2.2250738585072014e-308,\n"
+ " -1.7976931348623157e+308,\n"
+ " -2.2204460492503131e-16,\n"
+ " -4.9406564584124654e-324,\n"
+ " 0,\n"
+ " 9007199254740992,\n"
+ " -9007199254740992\n"
+ " ]\n"
+ "}\n";
+
+ QCOMPARE(json, expected);
+
+ JsonDocument doc;
+ doc.setObject(object);
+ json = doc.toJson();
+ QCOMPARE(json, expected);
+}
+
+void tst_Json::fromJson()
+{
+ {
+ std::string json = "[\n true\n]\n";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 1);
+ QCOMPARE(array.at(0).type(), JsonValue::Bool);
+ QCOMPARE(array.at(0).toBool(), true);
+ QCOMPARE(doc.toJson(), json);
+ }
+ {
+ //regression test: test if unicode_control_characters are correctly decoded
+ std::string json = "[\n \"" UNICODE_NON_CHARACTER "\"\n]\n";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 1);
+ QCOMPARE(array.at(0).type(), JsonValue::String);
+ QCOMPARE(array.at(0).toString(), std::string(UNICODE_NON_CHARACTER));
+ QCOMPARE(doc.toJson(), json);
+ }
+ {
+ std::string json = "[]";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 0);
+ }
+ {
+ std::string json = "{}";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), true);
+ JsonObject object = doc.object();
+ QCOMPARE(object.size(), 0);
+ }
+ {
+ std::string json = "{\n \"Key\": true\n}\n";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), true);
+ JsonObject object = doc.object();
+ QCOMPARE(object.size(), 1);
+ QCOMPARE(object.value("Key"), JsonValue(true));
+ QCOMPARE(doc.toJson(), json);
+ }
+ {
+ std::string json = "[ null, true, false, \"Foo\", 1, [], {} ]";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 7);
+ QCOMPARE(array.at(0).type(), JsonValue::Null);
+ QCOMPARE(array.at(1).type(), JsonValue::Bool);
+ QCOMPARE(array.at(1).toBool(), true);
+ QCOMPARE(array.at(2).type(), JsonValue::Bool);
+ QCOMPARE(array.at(2).toBool(), false);
+ QCOMPARE(array.at(3).type(), JsonValue::String);
+ QCOMPARE(array.at(3).toString(), std::string("Foo"));
+ QCOMPARE(array.at(4).type(), JsonValue::Double);
+ QCOMPARE(array.at(4).toDouble(), 1.);
+ QCOMPARE(array.at(5).type(), JsonValue::Array);
+ QCOMPARE(array.at(5).toArray().size(), 0);
+ QCOMPARE(array.at(6).type(), JsonValue::Object);
+ QCOMPARE(array.at(6).toObject().size(), 0);
+ }
+ {
+ std::string json = "{ \"0\": null, \"1\": true, \"2\": false, \"3\": \"Foo\", \"4\": 1, \"5\": [], \"6\": {} }";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), true);
+ JsonObject object = doc.object();
+ QCOMPARE(object.size(), 7);
+ QCOMPARE(object.value("0").type(), JsonValue::Null);
+ QCOMPARE(object.value("1").type(), JsonValue::Bool);
+ QCOMPARE(object.value("1").toBool(), true);
+ QCOMPARE(object.value("2").type(), JsonValue::Bool);
+ QCOMPARE(object.value("2").toBool(), false);
+ QCOMPARE(object.value("3").type(), JsonValue::String);
+ QCOMPARE(object.value("3").toString(), std::string("Foo"));
+ QCOMPARE(object.value("4").type(), JsonValue::Double);
+ QCOMPARE(object.value("4").toDouble(), 1.);
+ QCOMPARE(object.value("5").type(), JsonValue::Array);
+ QCOMPARE(object.value("5").toArray().size(), 0);
+ QCOMPARE(object.value("6").type(), JsonValue::Object);
+ QCOMPARE(object.value("6").toObject().size(), 0);
+ }
+ {
+ std::string compactJson = "{\"Array\": [true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\": \"Value\",\"null\": null}";
+ JsonDocument doc = JsonDocument::fromJson(compactJson);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), true);
+ JsonObject object = doc.object();
+ QCOMPARE(object.size(), 3);
+ QCOMPARE(object.value("\\Key\n").isString(), true);
+ QCOMPARE(object.value("\\Key\n").toString(), std::string("Value"));
+ QCOMPARE(object.value("null").isNull(), true);
+ QCOMPARE(object.value("Array").isArray(), true);
+ JsonArray array = object.value("Array").toArray();
+ QCOMPARE(array.size(), 5);
+ QCOMPARE(array.at(0).isBool(), true);
+ QCOMPARE(array.at(0).toBool(), true);
+ QCOMPARE(array.at(1).isDouble(), true);
+ QCOMPARE(array.at(1).toDouble(), 999.);
+ QCOMPARE(array.at(2).isString(), true);
+ QCOMPARE(array.at(2).toString(), std::string("string"));
+ QCOMPARE(array.at(3).isNull(), true);
+ QCOMPARE(array.at(4).isString(), true);
+ QCOMPARE(array.at(4).toString(), std::string("\\\a\n\r\b\tabcABC\""));
+ }
+}
+
+void tst_Json::fromJsonErrors()
+{
+ {
+ JsonParseError error;
+ std::string json = "{\n \n\n";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::UnterminatedObject);
+ QCOMPARE(error.offset, 8);
+ }
+ {
+ JsonParseError error;
+ std::string json = "{\n \"key\" 10\n";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::MissingNameSeparator);
+ QCOMPARE(error.offset, 13);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n \n\n";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::UnterminatedArray);
+ QCOMPARE(error.offset, 8);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n 1, true\n\n";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::UnterminatedArray);
+ QCOMPARE(error.offset, 14);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n 1 true\n\n";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::MissingValueSeparator);
+ QCOMPARE(error.offset, 7);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n nul";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalValue);
+ QCOMPARE(error.offset, 7);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n nulzz";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalValue);
+ QCOMPARE(error.offset, 10);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n tru";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalValue);
+ QCOMPARE(error.offset, 7);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n trud]";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalValue);
+ QCOMPARE(error.offset, 10);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n fal";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalValue);
+ QCOMPARE(error.offset, 7);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n falsd]";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalValue);
+ QCOMPARE(error.offset, 11);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n 11111";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::TerminationByNumber);
+ QCOMPARE(error.offset, 11);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n -1E10000]";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalNumber);
+ QCOMPARE(error.offset, 14);
+ }
+ {
+ /*
+ JsonParseError error;
+ std::string json = "[\n -1e-10000]";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalNumber);
+ QCOMPARE(error.offset, 15);
+ */
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n \"\\u12\"]";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalEscapeSequence);
+ QCOMPARE(error.offset, 11);
+ }
+ {
+ // This is not caught by the new parser as we don't parse
+ // UTF-8 anymore, but pass it as opaque blob.
+// JsonParseError error;
+// std::string json = "[\n \"foo" INVALID_UNICODE "bar\"]";
+// JsonDocument doc = JsonDocument::fromJson(json, &error);
+// QVERIFY(doc.isEmpty());
+// QCOMPARE(error.error, JsonParseError::IllegalUTF8String);
+// QCOMPARE(error.offset, 12);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n \"";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::UnterminatedString);
+ QCOMPARE(error.offset, 8);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n \"c" UNICODE_DJE "a\\u12\"]";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::IllegalEscapeSequence);
+ QCOMPARE(error.offset, 15);
+ }
+ {
+ // This is not caught by the new parser as we don't parse
+ // UTF-8 anymore, but pass it as opaque blob.
+// JsonParseError error;
+// std::string json = "[\n \"c" UNICODE_DJE "a" INVALID_UNICODE "bar\"]";
+// JsonDocument doc = JsonDocument::fromJson(json, &error);
+// QVERIFY(doc.isEmpty());
+// QCOMPARE(error.error, JsonParseError::IllegalUTF8String);
+// QCOMPARE(error.offset, 13);
+ }
+ {
+ JsonParseError error;
+ std::string json = "[\n \"c" UNICODE_DJE "a ]";
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, JsonParseError::UnterminatedString);
+ QCOMPARE(error.offset, 14);
+ }
+}
+
+void tst_Json::fromBinary()
+{
+ QFile file(testDataDir + QLatin1String("/test.json"));
+ file.open(QFile::ReadOnly);
+ std::string testJson = file.readAll().data();
+
+ JsonDocument doc = JsonDocument::fromJson(testJson);
+ JsonDocument outdoc = JsonDocument::fromBinaryData(doc.toBinaryData());
+ QVERIFY(!outdoc.isNull());
+ QCOMPARE(doc, outdoc);
+
+// // Can be used to re-create test.bjson:
+// QFile b1file(testDataDir + QLatin1String("/test.bjson.x"));
+// b1file.open(QFile::WriteOnly);
+// std::string d = doc.toBinaryData();
+// b1file.write(d.data(), d.size());
+// b1file.close();
+
+ QFile bfile(testDataDir + QLatin1String("/test.bjson"));
+ bfile.open(QFile::ReadOnly);
+ std::string binary = bfile.readAll().toStdString();
+
+ JsonDocument bdoc = JsonDocument::fromBinaryData(binary);
+ QVERIFY(!bdoc.isNull());
+ QCOMPARE(doc, bdoc);
+}
+
+void tst_Json::toAndFromBinary_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::newRow("test.json") << (testDataDir + QLatin1String("/test.json"));
+ QTest::newRow("test2.json") << (testDataDir + QLatin1String("/test2.json"));
+}
+
+void tst_Json::toAndFromBinary()
+{
+ QFETCH(QString, filename);
+ QFile file(filename);
+ QVERIFY(file.open(QFile::ReadOnly));
+ std::string data = file.readAll().data();
+
+ JsonDocument doc = JsonDocument::fromJson(data);
+ QVERIFY(!doc.isNull());
+ JsonDocument outdoc = JsonDocument::fromBinaryData(doc.toBinaryData());
+ QVERIFY(!outdoc.isNull());
+ QCOMPARE(doc, outdoc);
+}
+
+void tst_Json::parseNumbers()
+{
+ {
+ // test number parsing
+ struct Numbers {
+ const char *str;
+ int n;
+ };
+ Numbers numbers [] = {
+ { "0", 0 },
+ { "1", 1 },
+ { "10", 10 },
+ { "-1", -1 },
+ { "100000", 100000 },
+ { "-999", -999 }
+ };
+ int size = sizeof(numbers)/sizeof(Numbers);
+ for (int i = 0; i < size; ++i) {
+ std::string json = "[ ";
+ json += numbers[i].str;
+ json += " ]";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 1);
+ JsonValue val = array.at(0);
+ QCOMPARE(val.type(), JsonValue::Double);
+ QCOMPARE(val.toDouble(), (double)numbers[i].n);
+ }
+ }
+ {
+ // test number parsing
+ struct Numbers {
+ const char *str;
+ double n;
+ };
+ Numbers numbers [] = {
+ { "0", 0 },
+ { "1", 1 },
+ { "10", 10 },
+ { "-1", -1 },
+ { "100000", 100000 },
+ { "-999", -999 },
+ { "1.1", 1.1 },
+ { "1e10", 1e10 },
+ { "-1.1", -1.1 },
+ { "-1e10", -1e10 },
+ { "-1E10", -1e10 },
+ { "1.1e10", 1.1e10 },
+ { "1.1e308", 1.1e308 },
+ { "-1.1e308", -1.1e308 },
+ { "1.1e-308", 1.1e-308 },
+ { "-1.1e-308", -1.1e-308 },
+ { "1.1e+308", 1.1e+308 },
+ { "-1.1e+308", -1.1e+308 },
+ { "1.e+308", 1.e+308 },
+ { "-1.e+308", -1.e+308 }
+ };
+ int size = sizeof(numbers)/sizeof(Numbers);
+ for (int i = 0; i < size; ++i) {
+ std::string json = "[ ";
+ json += numbers[i].str;
+ json += " ]";
+ JsonDocument doc = JsonDocument::fromJson(json);
+#ifdef Q_OS_QNX
+ if (0 == QString::compare(numbers[i].str, "1.1e-308"))
+ QEXPECT_FAIL("", "See QTBUG-37066", Abort);
+#endif
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 1);
+ JsonValue val = array.at(0);
+ QCOMPARE(val.type(), JsonValue::Double);
+ QCOMPARE(val.toDouble(), numbers[i].n);
+ }
+ }
+}
+
+void tst_Json::parseStrings()
+{
+ const char *strings [] =
+ {
+ "Foo",
+ "abc\\\"abc",
+ "abc\\\\abc",
+ "abc\\babc",
+ "abc\\fabc",
+ "abc\\nabc",
+ "abc\\rabc",
+ "abc\\tabc",
+ "abc\\u0019abc",
+ "abc" UNICODE_DJE "abc",
+ UNICODE_NON_CHARACTER
+ };
+ int size = sizeof(strings)/sizeof(const char *);
+
+ for (int i = 0; i < size; ++i) {
+ std::string json = "[\n \"";
+ json += strings[i];
+ json += "\"\n]\n";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 1);
+ JsonValue val = array.at(0);
+ QCOMPARE(val.type(), JsonValue::String);
+
+ QCOMPARE(doc.toJson(), json);
+ }
+
+ struct Pairs {
+ const char *in;
+ const char *out;
+ };
+ Pairs pairs [] = {
+ { "abc\\/abc", "abc/abc" },
+ { "abc\\u0402abc", "abc" UNICODE_DJE "abc" },
+ { "abc\\u0065abc", "abceabc" },
+ { "abc\\uFFFFabc", "abc" UNICODE_NON_CHARACTER "abc" }
+ };
+ size = sizeof(pairs)/sizeof(Pairs);
+
+ for (int i = 0; i < size; ++i) {
+ std::string json = "[\n \"";
+ json += pairs[i].in;
+ json += "\"\n]\n";
+ std::string out = "[\n \"";
+ out += pairs[i].out;
+ out += "\"\n]\n";
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ JsonArray array = doc.array();
+ QCOMPARE(array.size(), 1);
+ JsonValue val = array.at(0);
+ QCOMPARE(val.type(), JsonValue::String);
+
+ QCOMPARE(doc.toJson(), out);
+ }
+
+}
+
+void tst_Json::parseDuplicateKeys()
+{
+ const char *json = "{ \"B\": true, \"A\": null, \"B\": false }";
+
+ JsonDocument doc = JsonDocument::fromJson(json);
+ QCOMPARE(doc.isObject(), true);
+
+ JsonObject o = doc.object();
+ QCOMPARE(o.size(), 2);
+ JsonObject::const_iterator it = o.constBegin();
+ QCOMPARE(it.key(), std::string("A"));
+ QCOMPARE(it.value(), JsonValue());
+ ++it;
+ QCOMPARE(it.key(), std::string("B"));
+ QCOMPARE(it.value(), JsonValue(false));
+}
+
+void tst_Json::testParser()
+{
+ QFile file(testDataDir + QLatin1String("/test.json"));
+ file.open(QFile::ReadOnly);
+ std::string testJson = file.readAll().data();
+
+ JsonDocument doc = JsonDocument::fromJson(testJson);
+ QVERIFY(!doc.isEmpty());
+}
+
+void tst_Json::compactArray()
+{
+ JsonArray array;
+ array.append("First Entry");
+ array.append("Second Entry");
+ array.append("Third Entry");
+ JsonDocument doc(array);
+ auto s = doc.toBinaryData().size();
+ array.removeAt(1);
+ doc.setArray(array);
+ QVERIFY(s > doc.toBinaryData().size());
+ s = doc.toBinaryData().size();
+ QCOMPARE(doc.toJson(),
+ std::string("[\n"
+ " \"First Entry\",\n"
+ " \"Third Entry\"\n"
+ "]\n"));
+
+ array.removeAt(0);
+ doc.setArray(array);
+ QVERIFY(s > doc.toBinaryData().size());
+ s = doc.toBinaryData().size();
+ QCOMPARE(doc.toJson(),
+ std::string("[\n"
+ " \"Third Entry\"\n"
+ "]\n"));
+
+ array.removeAt(0);
+ doc.setArray(array);
+ QVERIFY(s > doc.toBinaryData().size());
+ s = doc.toBinaryData().size();
+ QCOMPARE(doc.toJson(),
+ std::string("[\n"
+ "]\n"));
+
+}
+
+void tst_Json::compactObject()
+{
+ JsonObject object;
+ object.insert("Key1", "First Entry");
+ object.insert("Key2", "Second Entry");
+ object.insert("Key3", "Third Entry");
+ JsonDocument doc(object);
+ auto s = doc.toBinaryData().size();
+ object.remove("Key2");
+ doc.setObject(object);
+ QVERIFY(s > doc.toBinaryData().size());
+ s = doc.toBinaryData().size();
+ QCOMPARE(doc.toJson(),
+ std::string("{\n"
+ " \"Key1\": \"First Entry\",\n"
+ " \"Key3\": \"Third Entry\"\n"
+ "}\n"));
+
+ object.remove("Key1");
+ doc.setObject(object);
+ QVERIFY(s > doc.toBinaryData().size());
+ s = doc.toBinaryData().size();
+ QCOMPARE(doc.toJson(),
+ std::string("{\n"
+ " \"Key3\": \"Third Entry\"\n"
+ "}\n"));
+
+ object.remove("Key3");
+ doc.setObject(object);
+ QVERIFY(s > doc.toBinaryData().size());
+ s = doc.toBinaryData().size();
+ QCOMPARE(doc.toJson(),
+ std::string("{\n"
+ "}\n"));
+
+}
+
+void tst_Json::validation()
+{
+ // this basically tests that we don't crash on corrupt data
+ QFile file(testDataDir + QLatin1String("/test.json"));
+ QVERIFY(file.open(QFile::ReadOnly));
+ std::string testJson = file.readAll().data();
+ QVERIFY(!testJson.empty());
+
+ JsonDocument doc = JsonDocument::fromJson(testJson);
+ QVERIFY(!doc.isNull());
+
+ std::string binary = doc.toBinaryData();
+
+ // only test the first 1000 bytes. Testing the full file takes too long
+ for (int i = 0; i < 1000; ++i) {
+ std::string corrupted = binary;
+ corrupted[i] = char(0xff);
+ JsonDocument doc = JsonDocument::fromBinaryData(corrupted);
+ if (doc.isNull())
+ continue;
+ std::string json = doc.toJson();
+ }
+
+
+ QFile file2(testDataDir + QLatin1String("/test3.json"));
+ file2.open(QFile::ReadOnly);
+ testJson = file2.readAll().data();
+ QVERIFY(!testJson.empty());
+
+ doc = JsonDocument::fromJson(testJson);
+ QVERIFY(!doc.isNull());
+
+ binary = doc.toBinaryData();
+
+ for (size_t i = 0; i < binary.size(); ++i) {
+ std::string corrupted = binary;
+ corrupted[i] = char(0xff);
+ JsonDocument doc = JsonDocument::fromBinaryData(corrupted);
+ if (doc.isNull())
+ continue;
+ std::string json = doc.toJson();
+
+ corrupted = binary;
+ corrupted[i] = 0x00;
+ doc = JsonDocument::fromBinaryData(corrupted);
+ if (doc.isNull())
+ continue;
+ json = doc.toJson();
+ }
+}
+
+void tst_Json::assignToDocument()
+{
+ {
+ const char *json = "{ \"inner\": { \"key\": true } }";
+ JsonDocument doc = JsonDocument::fromJson(json);
+
+ JsonObject o = doc.object();
+ JsonValue inner = o.value("inner");
+
+ JsonDocument innerDoc(inner.toObject());
+
+ QVERIFY(innerDoc != doc);
+ QCOMPARE(innerDoc.object(), inner.toObject());
+ }
+ {
+ const char *json = "[ [ true ] ]";
+ JsonDocument doc = JsonDocument::fromJson(json);
+
+ JsonArray a = doc.array();
+ JsonValue inner = a.at(0);
+
+ JsonDocument innerDoc(inner.toArray());
+
+ QVERIFY(innerDoc != doc);
+ QCOMPARE(innerDoc.array(), inner.toArray());
+ }
+}
+
+
+void tst_Json::testDuplicateKeys()
+{
+ JsonObject obj;
+ obj.insert("foo", "bar");
+ obj.insert("foo", "zap");
+ QCOMPARE(obj.size(), 1);
+ QCOMPARE(obj.value("foo").toString(), std::string("zap"));
+}
+
+void tst_Json::testCompaction()
+{
+ // modify object enough times to trigger compactionCounter
+ // and make sure the data is still valid
+ JsonObject obj;
+ for (int i = 0; i < 33; ++i) {
+ obj.remove("foo");
+ obj.insert("foo", "bar");
+ }
+ QCOMPARE(obj.size(), 1);
+ QCOMPARE(obj.value("foo").toString(), std::string("bar"));
+
+ JsonDocument doc = JsonDocument::fromBinaryData(JsonDocument(obj).toBinaryData());
+ QVERIFY(!doc.isNull());
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), false);
+ QCOMPARE(doc.isObject(), true);
+ QCOMPARE(doc.object(), obj);
+}
+
+void tst_Json::testCompactionError()
+{
+ JsonObject schemaObject;
+ schemaObject.insert("_Type", "_SchemaType");
+ schemaObject.insert("name", "Address");
+ schemaObject.insert("schema", JsonObject());
+ {
+ JsonObject content(schemaObject);
+ JsonDocument doc(content);
+ QVERIFY(!doc.isNull());
+ QByteArray hash = QCryptographicHash::hash(doc.toBinaryData().data(), QCryptographicHash::Md5).toHex();
+ schemaObject.insert("_Version", hash.data());
+ }
+
+ JsonObject schema;
+ schema.insert("streetNumber", schema.value("number").toObject());
+ schemaObject.insert("schema", schema);
+ {
+ JsonObject content(schemaObject);
+ content.remove("_Uuid");
+ content.remove("_Version");
+ JsonDocument doc(content);
+ QVERIFY(!doc.isNull());
+ QByteArray hash = QCryptographicHash::hash(doc.toBinaryData().data(), QCryptographicHash::Md5).toHex();
+ schemaObject.insert("_Version", hash.data());
+ }
+}
+
+void tst_Json::parseUnicodeEscapes()
+{
+ const std::string json = "[ \"A\\u00e4\\u00C4\" ]";
+
+ JsonDocument doc = JsonDocument::fromJson(json);
+ JsonArray array = doc.array();
+
+ QString result = QLatin1String("A");
+ result += QChar(0xe4);
+ result += QChar(0xc4);
+
+ std::string expected = result.toUtf8().data();
+
+ QCOMPARE(array.first().toString(), expected);
+}
+
+void tst_Json::assignObjects()
+{
+ const char *json =
+ "[ { \"Key\": 1 }, { \"Key\": 2 } ]";
+
+ JsonDocument doc = JsonDocument::fromJson(json);
+ JsonArray array = doc.array();
+
+ JsonObject object = array.at(0).toObject();
+ QCOMPARE(object.value("Key").toDouble(), 1.);
+
+ object = array.at(1).toObject();
+ QCOMPARE(object.value("Key").toDouble(), 2.);
+}
+
+void tst_Json::assignArrays()
+{
+ const char *json =
+ "[ [ 1 ], [ 2 ] ]";
+
+ JsonDocument doc = JsonDocument::fromJson(json);
+ JsonArray array = doc.array();
+
+ JsonArray inner = array.at(0).toArray() ;
+ QCOMPARE(inner.at(0).toDouble(), 1.);
+
+ inner= array.at(1).toArray();
+ QCOMPARE(inner.at(0).toDouble(), 2.);
+}
+
+void tst_Json::testTrailingComma()
+{
+ const char *jsons[] = { "{ \"Key\": 1, }", "[ { \"Key\": 1 }, ]" };
+
+ for (unsigned i = 0; i < sizeof(jsons)/sizeof(jsons[0]); ++i) {
+ JsonParseError error;
+ JsonDocument doc = JsonDocument::fromJson(jsons[i], &error);
+ QCOMPARE(error.error, JsonParseError::MissingObject);
+ }
+}
+
+void tst_Json::testDetachBug()
+{
+ JsonObject dynamic;
+ JsonObject embedded;
+
+ JsonObject local;
+
+ embedded.insert("Key1", "Value1");
+ embedded.insert("Key2", "Value2");
+ dynamic.insert("Bogus", "bogusValue");
+ dynamic.insert("embedded", embedded);
+ local = dynamic.value("embedded").toObject();
+
+ dynamic.remove("embedded");
+
+ QCOMPARE(local.keys().size(), size_t(2));
+ local.remove("Key1");
+ local.remove("Key2");
+ QCOMPARE(local.keys().size(), size_t(0));
+
+ local.insert("Key1", "anotherValue");
+ QCOMPARE(local.keys().size(), size_t(1));
+}
+
+void tst_Json::valueEquals()
+{
+ QCOMPARE(JsonValue(), JsonValue());
+ QVERIFY(JsonValue() != JsonValue(JsonValue::Undefined));
+ QVERIFY(JsonValue() != JsonValue(true));
+ QVERIFY(JsonValue() != JsonValue(1.));
+ QVERIFY(JsonValue() != JsonValue(JsonArray()));
+ QVERIFY(JsonValue() != JsonValue(JsonObject()));
+
+ QCOMPARE(JsonValue(true), JsonValue(true));
+ QVERIFY(JsonValue(true) != JsonValue(false));
+ QVERIFY(JsonValue(true) != JsonValue(JsonValue::Undefined));
+ QVERIFY(JsonValue(true) != JsonValue());
+ QVERIFY(JsonValue(true) != JsonValue(1.));
+ QVERIFY(JsonValue(true) != JsonValue(JsonArray()));
+ QVERIFY(JsonValue(true) != JsonValue(JsonObject()));
+
+ QCOMPARE(JsonValue(1), JsonValue(1));
+ QVERIFY(JsonValue(1) != JsonValue(2));
+ QCOMPARE(JsonValue(1), JsonValue(1.));
+ QVERIFY(JsonValue(1) != JsonValue(1.1));
+ QVERIFY(JsonValue(1) != JsonValue(JsonValue::Undefined));
+ QVERIFY(JsonValue(1) != JsonValue());
+ QVERIFY(JsonValue(1) != JsonValue(true));
+ QVERIFY(JsonValue(1) != JsonValue(JsonArray()));
+ QVERIFY(JsonValue(1) != JsonValue(JsonObject()));
+
+ QCOMPARE(JsonValue(1.), JsonValue(1.));
+ QVERIFY(JsonValue(1.) != JsonValue(2.));
+ QVERIFY(JsonValue(1.) != JsonValue(JsonValue::Undefined));
+ QVERIFY(JsonValue(1.) != JsonValue());
+ QVERIFY(JsonValue(1.) != JsonValue(true));
+ QVERIFY(JsonValue(1.) != JsonValue(JsonArray()));
+ QVERIFY(JsonValue(1.) != JsonValue(JsonObject()));
+
+ QCOMPARE(JsonValue(JsonArray()), JsonValue(JsonArray()));
+ JsonArray nonEmptyArray;
+ nonEmptyArray.append(true);
+ QVERIFY(JsonValue(JsonArray()) != nonEmptyArray);
+ QVERIFY(JsonValue(JsonArray()) != JsonValue(JsonValue::Undefined));
+ QVERIFY(JsonValue(JsonArray()) != JsonValue());
+ QVERIFY(JsonValue(JsonArray()) != JsonValue(true));
+ QVERIFY(JsonValue(JsonArray()) != JsonValue(1.));
+ QVERIFY(JsonValue(JsonArray()) != JsonValue(JsonObject()));
+
+ QCOMPARE(JsonValue(JsonObject()), JsonValue(JsonObject()));
+ JsonObject nonEmptyObject;
+ nonEmptyObject.insert("Key", true);
+ QVERIFY(JsonValue(JsonObject()) != nonEmptyObject);
+ QVERIFY(JsonValue(JsonObject()) != JsonValue(JsonValue::Undefined));
+ QVERIFY(JsonValue(JsonObject()) != JsonValue());
+ QVERIFY(JsonValue(JsonObject()) != JsonValue(true));
+ QVERIFY(JsonValue(JsonObject()) != JsonValue(1.));
+ QVERIFY(JsonValue(JsonObject()) != JsonValue(JsonArray()));
+
+ QCOMPARE(JsonValue("foo"), JsonValue("foo"));
+ QCOMPARE(JsonValue("\x66\x6f\x6f"), JsonValue("foo"));
+ QCOMPARE(JsonValue("\x62\x61\x72"), JsonValue("bar"));
+ QCOMPARE(JsonValue(UNICODE_NON_CHARACTER), JsonValue(UNICODE_NON_CHARACTER));
+ QCOMPARE(JsonValue(UNICODE_DJE), JsonValue(UNICODE_DJE));
+ QCOMPARE(JsonValue("\xc3\xa9"), JsonValue("\xc3\xa9"));
+}
+
+void tst_Json::objectEquals_data()
+{
+ QTest::addColumn<JsonObject>("left");
+ QTest::addColumn<JsonObject>("right");
+ QTest::addColumn<bool>("result");
+
+ QTest::newRow("two defaults") << JsonObject() << JsonObject() << true;
+
+ JsonObject object1;
+ object1.insert("property", 1);
+ JsonObject object2;
+ object2["property"] = 1;
+ JsonObject object3;
+ object3.insert("property1", 1);
+ object3.insert("property2", 2);
+
+ QTest::newRow("the same object (1 vs 2)") << object1 << object2 << true;
+ QTest::newRow("the same object (3 vs 3)") << object3 << object3 << true;
+ QTest::newRow("different objects (2 vs 3)") << object2 << object3 << false;
+ QTest::newRow("object vs default") << object1 << JsonObject() << false;
+
+ JsonObject empty;
+ empty.insert("property", 1);
+ empty.take("property");
+ QTest::newRow("default vs empty") << JsonObject() << empty << true;
+ QTest::newRow("empty vs empty") << empty << empty << true;
+ QTest::newRow("object vs empty") << object1 << empty << false;
+
+ JsonObject referencedEmpty;
+ referencedEmpty["undefined"];
+ QTest::newRow("referenced empty vs referenced empty") << referencedEmpty << referencedEmpty << true;
+ QTest::newRow("referenced empty vs object") << referencedEmpty << object1 << false;
+
+ JsonObject referencedObject1;
+ referencedObject1.insert("property", 1);
+ referencedObject1["undefined"];
+ JsonObject referencedObject2;
+ referencedObject2.insert("property", 1);
+ referencedObject2["aaaaaaaaa"]; // earlier then "property"
+ referencedObject2["zzzzzzzzz"]; // after "property"
+ QTest::newRow("referenced object vs default") << referencedObject1 << JsonObject() << false;
+ QTest::newRow("referenced object vs referenced object") << referencedObject1 << referencedObject1 << true;
+ QTest::newRow("referenced object vs object (different)") << referencedObject1 << object3 << false;
+}
+
+void tst_Json::objectEquals()
+{
+ QFETCH(JsonObject, left);
+ QFETCH(JsonObject, right);
+ QFETCH(bool, result);
+
+ QCOMPARE(left == right, result);
+ QCOMPARE(right == left, result);
+
+ // invariants checks
+ QCOMPARE(left, left);
+ QCOMPARE(right, right);
+ QCOMPARE(left != right, !result);
+ QCOMPARE(right != left, !result);
+
+ // The same but from JsonValue perspective
+ QCOMPARE(JsonValue(left) == JsonValue(right), result);
+ QCOMPARE(JsonValue(left) != JsonValue(right), !result);
+ QCOMPARE(JsonValue(right) == JsonValue(left), result);
+ QCOMPARE(JsonValue(right) != JsonValue(left), !result);
+}
+
+void tst_Json::arrayEquals_data()
+{
+ QTest::addColumn<JsonArray>("left");
+ QTest::addColumn<JsonArray>("right");
+ QTest::addColumn<bool>("result");
+
+ QTest::newRow("two defaults") << JsonArray() << JsonArray() << true;
+
+ JsonArray array1;
+ array1.append(1);
+ JsonArray array2;
+ array2.append(2111);
+ array2[0] = 1;
+ JsonArray array3;
+ array3.insert(0, 1);
+ array3.insert(1, 2);
+
+ QTest::newRow("the same array (1 vs 2)") << array1 << array2 << true;
+ QTest::newRow("the same array (3 vs 3)") << array3 << array3 << true;
+ QTest::newRow("different arrays (2 vs 3)") << array2 << array3 << false;
+ QTest::newRow("array vs default") << array1 << JsonArray() << false;
+
+ JsonArray empty;
+ empty.append(1);
+ empty.takeAt(0);
+ QTest::newRow("default vs empty") << JsonArray() << empty << true;
+ QTest::newRow("empty vs default") << empty << JsonArray() << true;
+ QTest::newRow("empty vs empty") << empty << empty << true;
+ QTest::newRow("array vs empty") << array1 << empty << false;
+}
+
+void tst_Json::arrayEquals()
+{
+ QFETCH(JsonArray, left);
+ QFETCH(JsonArray, right);
+ QFETCH(bool, result);
+
+ QCOMPARE(left == right, result);
+ QCOMPARE(right == left, result);
+
+ // invariants checks
+ QCOMPARE(left, left);
+ QCOMPARE(right, right);
+ QCOMPARE(left != right, !result);
+ QCOMPARE(right != left, !result);
+
+ // The same but from JsonValue perspective
+ QCOMPARE(JsonValue(left) == JsonValue(right), result);
+ QCOMPARE(JsonValue(left) != JsonValue(right), !result);
+ QCOMPARE(JsonValue(right) == JsonValue(left), result);
+ QCOMPARE(JsonValue(right) != JsonValue(left), !result);
+}
+
+void tst_Json::bom()
+{
+ QFile file(testDataDir + QLatin1String("/bom.json"));
+ file.open(QFile::ReadOnly);
+ std::string json = file.readAll().data();
+
+ // Import json document into a JsonDocument
+ JsonParseError error;
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+
+ QVERIFY(!doc.isNull());
+ QCOMPARE(error.error, JsonParseError::NoError);
+}
+
+void tst_Json::nesting()
+{
+ // check that we abort parsing too deeply nested json documents.
+ // this is to make sure we don't crash because the parser exhausts the
+ // stack.
+
+ const char *array_data =
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
+ "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]";
+
+ std::string json(array_data);
+ JsonParseError error;
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+
+ QVERIFY(!doc.isNull());
+ QCOMPARE(error.error, JsonParseError::NoError);
+
+ json = '[' + json + ']';
+ doc = JsonDocument::fromJson(json, &error);
+
+ QVERIFY(doc.isNull());
+ QCOMPARE(error.error, JsonParseError::DeepNesting);
+
+ json = std::string("true ");
+
+ for (int i = 0; i < 1024; ++i)
+ json = "{ \"Key\": " + json + " }";
+
+ doc = JsonDocument::fromJson(json, &error);
+
+ QVERIFY(!doc.isNull());
+ QCOMPARE(error.error, JsonParseError::NoError);
+
+ json = '[' + json + ']';
+ doc = JsonDocument::fromJson(json, &error);
+
+ QVERIFY(doc.isNull());
+ QCOMPARE(error.error, JsonParseError::DeepNesting);
+
+}
+
+void tst_Json::longStrings()
+{
+#if 0
+ // test around 15 and 16 bit boundaries, as these are limits
+ // in the data structures (for Latin1String in qjson_p.h)
+ QString s(0x7ff0, 'a');
+ for (int i = 0x7ff0; i < 0x8010; i++) {
+ s.append(QLatin1Char('c'));
+
+ QMap <QString, QVariant> map;
+ map["key"] = s;
+
+ /* Create a JsonDocument from the QMap ... */
+ JsonDocument d1 = JsonDocument::fromVariant(QVariant(map));
+ /* ... and a std::string from the JsonDocument */
+ std::string a1 = d1.toJson();
+
+ /* Create a JsonDocument from the std::string ... */
+ JsonDocument d2 = JsonDocument::fromJson(a1);
+ /* ... and a std::string from the JsonDocument */
+ std::string a2 = d2.toJson();
+ QCOMPARE(a1, a2);
+ }
+
+ s = QString(0xfff0, 'a');
+ for (int i = 0xfff0; i < 0x10010; i++) {
+ s.append(QLatin1Char('c'));
+
+ QMap <QString, QVariant> map;
+ map["key"] = s;
+
+ /* Create a JsonDocument from the QMap ... */
+ JsonDocument d1 = JsonDocument::fromVariant(QVariant(map));
+ /* ... and a std::string from the JsonDocument */
+ std::string a1 = d1.toJson();
+
+ /* Create a JsonDocument from the std::string ... */
+ JsonDocument d2 = JsonDocument::fromJson(a1);
+ /* ... and a std::string from the JsonDocument */
+ std::string a2 = d2.toJson();
+ QCOMPARE(a1, a2);
+ }
+#endif
+}
+
+void tst_Json::testJsonValueRefDefault()
+{
+ JsonObject empty;
+
+ QCOMPARE(empty["n/a"].toString(), std::string());
+ QCOMPARE(empty["n/a"].toString("default"), std::string("default"));
+
+ QCOMPARE(empty["n/a"].toBool(), false);
+ QCOMPARE(empty["n/a"].toBool(true), true);
+
+ QCOMPARE(empty["n/a"].toInt(), 0);
+ QCOMPARE(empty["n/a"].toInt(42), 42);
+
+ QCOMPARE(empty["n/a"].toDouble(), 0.0);
+ QCOMPARE(empty["n/a"].toDouble(42.0), 42.0);
+}
+
+void tst_Json::arrayInitializerList()
+{
+#ifndef Q_COMPILER_INITIALIZER_LISTS
+ QSKIP("initializer_list is enabled only with c++11 support");
+#else
+ QVERIFY(JsonArray{}.isEmpty());
+ QCOMPARE(JsonArray{"one"}.count(), 1);
+ QCOMPARE(JsonArray{1}.count(), 1);
+
+ {
+ JsonArray a{1.3, "hello", 0};
+ QCOMPARE(JsonValue(a[0]), JsonValue(1.3));
+ QCOMPARE(JsonValue(a[1]), JsonValue("hello"));
+ QCOMPARE(JsonValue(a[2]), JsonValue(0));
+ QCOMPARE(a.count(), 3);
+ }
+ {
+ JsonObject o;
+ o["property"] = 1;
+ JsonArray a1 {o};
+ QCOMPARE(a1.count(), 1);
+ QCOMPARE(a1[0].toObject(), o);
+
+ JsonArray a2 {o, 23};
+ QCOMPARE(a2.count(), 2);
+ QCOMPARE(a2[0].toObject(), o);
+ QCOMPARE(JsonValue(a2[1]), JsonValue(23));
+
+ JsonArray a3 { a1, o, a2 };
+ QCOMPARE(JsonValue(a3[0]), JsonValue(a1));
+ QCOMPARE(JsonValue(a3[1]), JsonValue(o));
+ QCOMPARE(JsonValue(a3[2]), JsonValue(a2));
+
+ JsonArray a4 { 1, JsonArray{1,2,3}, JsonArray{"hello", 2}, JsonObject{{"one", 1}} };
+ QCOMPARE(a4.count(), 4);
+ QCOMPARE(JsonValue(a4[0]), JsonValue(1));
+
+ {
+ JsonArray a41 = a4[1].toArray();
+ JsonArray a42 = a4[2].toArray();
+ JsonObject a43 = a4[3].toObject();
+ QCOMPARE(a41.count(), 3);
+ QCOMPARE(a42.count(), 2);
+ QCOMPARE(a43.count(), 1);
+
+ QCOMPARE(JsonValue(a41[2]), JsonValue(3));
+ QCOMPARE(JsonValue(a42[1]), JsonValue(2));
+ QCOMPARE(JsonValue(a43["one"]), JsonValue(1));
+ }
+ }
+#endif
+}
+
+void tst_Json::objectInitializerList()
+{
+#ifndef Q_COMPILER_INITIALIZER_LISTS
+ QSKIP("initializer_list is enabled only with c++11 support");
+#else
+ QVERIFY(JsonObject{}.isEmpty());
+
+ { // one property
+ JsonObject one {{"one", 1}};
+ QCOMPARE(one.count(), 1);
+ QVERIFY(one.contains("one"));
+ QCOMPARE(JsonValue(one["one"]), JsonValue(1));
+ }
+ { // two properties
+ JsonObject two {
+ {"one", 1},
+ {"two", 2}
+ };
+ QCOMPARE(two.count(), 2);
+ QVERIFY(two.contains("one"));
+ QVERIFY(two.contains("two"));
+ QCOMPARE(JsonValue(two["one"]), JsonValue(1));
+ QCOMPARE(JsonValue(two["two"]), JsonValue(2));
+ }
+ { // nested object
+ JsonObject object{{"nested", JsonObject{{"innerProperty", 2}}}};
+ QCOMPARE(object.count(), 1);
+ QVERIFY(object.contains("nested"));
+ QVERIFY(object["nested"].isObject());
+
+ JsonObject nested = object["nested"].toObject();
+ QCOMPARE(JsonValue(nested["innerProperty"]), JsonValue(2));
+ }
+ { // nested array
+ JsonObject object{{"nested", JsonArray{"innerValue", 2.1, "bum cyk cyk"}}};
+ QCOMPARE(object.count(), 1);
+ QVERIFY(object.contains("nested"));
+ QVERIFY(object["nested"].isArray());
+
+ JsonArray nested = object["nested"].toArray();
+ QCOMPARE(nested.count(), 3);
+ QCOMPARE(JsonValue(nested[0]), JsonValue("innerValue"));
+ QCOMPARE(JsonValue(nested[1]), JsonValue(2.1));
+ }
+#endif
+}
+
+void tst_Json::unicodeKeys()
+{
+ std::string json = "{"
+ "\"x\\u2090_1\": \"hello_1\","
+ "\"y\\u2090_2\": \"hello_2\","
+ "\"T\\u2090_3\": \"hello_3\","
+ "\"xyz_4\": \"hello_4\","
+ "\"abc_5\": \"hello_5\""
+ "}";
+
+ JsonParseError error;
+ JsonDocument doc = JsonDocument::fromJson(json, &error);
+ QCOMPARE(error.error, JsonParseError::NoError);
+ JsonObject o = doc.object();
+
+ QCOMPARE(o.keys().size(), size_t(5));
+ Q_FOREACH (const std::string &k, o.keys()) {
+ QByteArray key(k.data());
+ std::string suffix = key.mid(key.indexOf('_')).data();
+ QCOMPARE(o[key.data()].toString(), "hello" + suffix);
+ }
+}
+
+void tst_Json::garbageAtEnd()
+{
+ JsonParseError error;
+ JsonDocument doc = JsonDocument::fromJson("{},", &error);
+ QCOMPARE(error.error, JsonParseError::GarbageAtEnd);
+ QCOMPARE(error.offset, 2);
+ QVERIFY(doc.isEmpty());
+
+ doc = JsonDocument::fromJson("{} ", &error);
+ QCOMPARE(error.error, JsonParseError::NoError);
+ QVERIFY(!doc.isEmpty());
+}
+
+void tst_Json::removeNonLatinKey()
+{
+ const std::string nonLatinKeyName = "Атрибут100500";
+
+ JsonObject sourceObject;
+
+ sourceObject.insert("code", 1);
+ sourceObject.remove("code");
+
+ sourceObject.insert(nonLatinKeyName, 1);
+
+ const std::string json = JsonDocument(sourceObject).toJson();
+ const JsonObject restoredObject = JsonDocument::fromJson(json).object();
+
+ QCOMPARE(sourceObject.keys(), restoredObject.keys());
+ QVERIFY(sourceObject.contains(nonLatinKeyName));
+ QVERIFY(restoredObject.contains(nonLatinKeyName));
+}
+
+QTEST_MAIN(tst_Json)
+
+#include "tst_json.moc"
diff --git a/tests/benchmarks/json/json.pro b/tests/benchmarks/json/json.pro
new file mode 100644
index 00000000000..93569682659
--- /dev/null
+++ b/tests/benchmarks/json/json.pro
@@ -0,0 +1,14 @@
+TARGET = tst_bench_json
+QT = core testlib
+CONFIG -= app_bundle
+
+SOURCES += tst_bench_json.cpp
+
+TESTDATA = numbers.json test.json
+
+
+INCLUDEPATH += ../../../src/shared/json
+
+#DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
+
+include(../../../src/shared/json/json.pri)
diff --git a/tests/benchmarks/json/numbers.json b/tests/benchmarks/json/numbers.json
new file mode 100644
index 00000000000..469156a78a3
--- /dev/null
+++ b/tests/benchmarks/json/numbers.json
@@ -0,0 +1,19 @@
+[
+ {
+ "integer": 1234567890,
+ "real": -9876.543210,
+ "e": 0.123456789e-12,
+ "E": 1.234567890E+34,
+ "": 23456789012E66,
+ "zero": 0,
+ "one": 1
+ },
+ [
+ -1234567890,
+ -1234567890,
+ -1234567890,
+ 1234567890,
+ 1234567890,
+ 1234567890
+ ]
+]
diff --git a/tests/benchmarks/json/test.json b/tests/benchmarks/json/test.json
new file mode 100644
index 00000000000..330756894a6
--- /dev/null
+++ b/tests/benchmarks/json/test.json
@@ -0,0 +1,66 @@
+[
+ "JSON Test Pattern pass1",
+ {"object with 1 member":["array with 1 element"]},
+ {},
+ [],
+ -42,
+ true,
+ false,
+ null,
+ {
+ "integer": 1234567890,
+ "real": -9876.543210,
+ "e": 0.123456789e-12,
+ "E": 1.234567890E+34,
+ "": 23456789012E66,
+ "zero": 0,
+ "one": 1,
+ "space": " ",
+ "quote": "\"",
+ "backslash": "\\",
+ "controls": "\b\f\n\r\t",
+ "slash": "/ & \/",
+ "alpha": "abcdefghijklmnopqrstuvwxyz",
+ "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "digit": "0123456789",
+ "0123456789": "digit",
+ "special": "`1~!@#$%^&*()_+-={\':[,]}|;.</>?",
+ "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
+ "true": true,
+ "false": false,
+ "null": null,
+ "array":[ ],
+ "object":{ },
+ "address": "50 St. James Street",
+ "url": "nix",
+ "comment": "// /* <!-- --",
+ "# -- --> */": " ",
+ " s p a c e d " :[1,2 , 3
+
+,
+
+4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
+ "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
+ "quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
+ "\/\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?" : "A key can be any string"
+ },
+ 0.5 ,98.6
+,
+99.44
+,
+
+1066,
+1e1,
+0.1e1,
+1e-1,
+1e00,
+2e+00,
+2e-00,
+"rosebud",
+{"foo": "bar"},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}},
+{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}
+]
+
diff --git a/tests/benchmarks/json/tst_bench_json.cpp b/tests/benchmarks/json/tst_bench_json.cpp
new file mode 100644
index 00000000000..02481950641
--- /dev/null
+++ b/tests/benchmarks/json/tst_bench_json.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <qjsondocument.h>
+#include <qjsonobject.h>
+
+#include <json.h>
+
+using namespace Json;
+
+class BenchmarkJson: public QObject
+{
+ Q_OBJECT
+
+public:
+ BenchmarkJson() {}
+
+private Q_SLOTS:
+ void jsonObjectInsertQt();
+ void jsonObjectInsertStd();
+
+ void createBinaryMessageQt();
+ void createBinaryMessageStd();
+
+ void readBinaryMessageQt();
+ void readBinaryMessageStd();
+
+ void createTextMessageQt();
+ void createTextMessageStd();
+
+ void readTextMessageQt();
+ void readTextMessageStd();
+
+ void parseJsonQt();
+ void parseJsonStd();
+
+ void parseNumbersQt();
+ void parseNumbersStd();
+};
+
+void BenchmarkJson::parseNumbersQt()
+{
+ QString testFile = QFINDTESTDATA("numbers.json");
+ QVERIFY2(!testFile.isEmpty(), "cannot find test file numbers.json!");
+ QFile file(testFile);
+ file.open(QFile::ReadOnly);
+ QByteArray testJson = file.readAll();
+
+ QBENCHMARK {
+ QJsonDocument doc = QJsonDocument::fromJson(testJson);
+ doc.object();
+ }
+}
+
+void BenchmarkJson::parseNumbersStd()
+{
+ QString testFile = QFINDTESTDATA("numbers.json");
+ QVERIFY2(!testFile.isEmpty(), "cannot find test file numbers.json!");
+ QFile file(testFile);
+ file.open(QFile::ReadOnly);
+ std::string testJson = file.readAll().toStdString();
+
+ QBENCHMARK {
+ JsonDocument doc = JsonDocument::fromJson(testJson);
+ doc.object();
+ }
+}
+
+void BenchmarkJson::parseJsonQt()
+{
+ QString testFile = QFINDTESTDATA("test.json");
+ QVERIFY2(!testFile.isEmpty(), "cannot find test file test.json!");
+ QFile file(testFile);
+ file.open(QFile::ReadOnly);
+ QByteArray testJson = file.readAll();
+
+ QBENCHMARK {
+ QJsonDocument doc = QJsonDocument::fromJson(testJson);
+ doc.object();
+ }
+}
+
+void BenchmarkJson::parseJsonStd()
+{
+ QString testFile = QFINDTESTDATA("test.json");
+ QVERIFY2(!testFile.isEmpty(), "cannot find test file test.json!");
+ QFile file(testFile);
+ file.open(QFile::ReadOnly);
+ std::string testJson = file.readAll().toStdString();
+
+ QBENCHMARK {
+ JsonDocument doc = JsonDocument::fromJson(testJson);
+ doc.object();
+ }
+}
+
+void BenchmarkJson::createBinaryMessageQt()
+{
+ // Example: send information over a datastream to another process
+ // Measure performance of creating and processing data into bytearray
+ QBENCHMARK {
+ QJsonObject ob;
+ ob.insert(QStringLiteral("command"), 1);
+ ob.insert(QStringLiteral("key"), "some information");
+ ob.insert(QStringLiteral("env"), "some environment variables");
+ QJsonDocument(ob).toBinaryData();
+ }
+}
+
+void BenchmarkJson::createBinaryMessageStd()
+{
+ // Example: send information over a datastream to another process
+ // Measure performance of creating and processing data into bytearray
+ QBENCHMARK {
+ JsonObject ob;
+ ob.insert("command", 1);
+ ob.insert("key", "some information");
+ ob.insert("env", "some environment variables");
+ JsonDocument(ob).toBinaryData();
+ }
+}
+
+void BenchmarkJson::readBinaryMessageQt()
+{
+ // Example: receive information over a datastream from another process
+ // Measure performance of converting content back to QVariantMap
+ // We need to recreate the bytearray but here we only want to measure the latter
+ QJsonObject ob;
+ ob.insert(QStringLiteral("command"), 1);
+ ob.insert(QStringLiteral("key"), "some information");
+ ob.insert(QStringLiteral("env"), "some environment variables");
+ QByteArray msg = QJsonDocument(ob).toBinaryData();
+
+ QBENCHMARK {
+ QJsonDocument::fromBinaryData(msg, QJsonDocument::Validate).object();
+ }
+}
+
+void BenchmarkJson::readBinaryMessageStd()
+{
+ // Example: receive information over a datastream from another process
+ // Measure performance of converting content back to QVariantMap
+ // We need to recreate the bytearray but here we only want to measure the latter
+ JsonObject ob;
+ ob.insert("command", 1);
+ ob.insert("key", "some information");
+ ob.insert("env", "some environment variables");
+ std::string msg = JsonDocument(ob).toBinaryData();
+
+ QBENCHMARK {
+ JsonDocument::fromBinaryData(msg, JsonDocument::Validate).object();
+ }
+}
+
+void BenchmarkJson::createTextMessageQt()
+{
+ // Example: send information over a datastream to another process
+ // Measure performance of creating and processing data into bytearray
+ QBENCHMARK {
+ QJsonObject ob;
+ ob.insert(QStringLiteral("command"), 1);
+ ob.insert(QStringLiteral("key"), "some information");
+ ob.insert(QStringLiteral("env"), "some environment variables");
+ QByteArray msg = QJsonDocument(ob).toJson();
+ }
+}
+
+void BenchmarkJson::createTextMessageStd()
+{
+ // Example: send information over a datastream to another process
+ // Measure performance of creating and processing data into bytearray
+ QBENCHMARK {
+ JsonObject ob;
+ ob.insert("command", 1);
+ ob.insert("key", "some information");
+ ob.insert("env", "some environment variables");
+ std::string msg = JsonDocument(ob).toJson();
+ }
+}
+
+void BenchmarkJson::readTextMessageQt()
+{
+ // Example: receive information over a datastream from another process
+ // Measure performance of converting content back to QVariantMap
+ // We need to recreate the bytearray but here we only want to measure the latter
+ QJsonObject ob;
+ ob.insert(QStringLiteral("command"), 1);
+ ob.insert(QStringLiteral("key"), "some information");
+ ob.insert(QStringLiteral("env"), "some environment variables");
+ QByteArray msg = QJsonDocument(ob).toJson();
+
+ QBENCHMARK {
+ QJsonDocument::fromJson(msg).object();
+ }
+}
+
+void BenchmarkJson::readTextMessageStd()
+{
+ // Example: receive information over a datastream from another process
+ // Measure performance of converting content back to QVariantMap
+ // We need to recreate the bytearray but here we only want to measure the latter
+ JsonObject ob;
+ ob.insert("command", 1);
+ ob.insert("key", "some information");
+ ob.insert("env", "some environment variables");
+ std::string msg = JsonDocument(ob).toJson();
+
+ QBENCHMARK {
+ JsonDocument::fromJson(msg).object();
+ }
+}
+
+void BenchmarkJson::jsonObjectInsertQt()
+{
+ QJsonObject object;
+ QJsonValue value(1.5);
+
+ QBENCHMARK {
+ for (int i = 0; i < 1000; i++)
+ object.insert("testkey_" + QString::number(i), value);
+ }
+}
+
+void BenchmarkJson::jsonObjectInsertStd()
+{
+ JsonObject object;
+ JsonValue value(1.5);
+
+ QBENCHMARK {
+ for (int i = 0; i < 1000; i++)
+ object.insert("testkey_" + std::to_string(i), value);
+ }
+}
+
+QTEST_MAIN(BenchmarkJson)
+
+#include "tst_bench_json.moc"
+