diff --git a/src/shared/json/README.md b/src/shared/json/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e641d8e5fe24b2ca3c91cdd9843f081866c723fd
--- /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 0000000000000000000000000000000000000000..6cb7502848a4120b15e15460d1ed73bcd2610e3f
--- /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 0000000000000000000000000000000000000000..7bc945cd3e90ee8acfd498e907e40e2a6f14087b
--- /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 0000000000000000000000000000000000000000..db7ce19eee96528ed43db871ee419afd36ea4770
--- /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 0000000000000000000000000000000000000000..3587d307afc437f6b34db1fc7661c892dc7f38f2
--- /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 5b8c62e332e15f19e620b3356ed7370560b8d9f2..1953849d02e062744fa5d966b133780e3888cef6 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 6284497e4c53b8e1a04389a37c733aab88e3c575..eabe10b3a19caab278b22be601a75ec16df3f01b 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 91c6e593f8874b15b65496fd070ee84525609459..15465a61e6fa261f5ea83a705db022fbbd2f3850 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 0000000000000000000000000000000000000000..d1e8d90e280cad3ed3d7e471a1df2bc2d3ec0a98
--- /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 0000000000000000000000000000000000000000..b7c5970698383cd3d75a54642b72695c7b06b7af
--- /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 0000000000000000000000000000000000000000..de9a22fcf2a6cb6a87da3db5235b13fbb4f4efca
--- /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 0000000000000000000000000000000000000000..ae12e5e446bb1305d45432a333f0a09aaa9d886d
Binary files /dev/null and b/tests/auto/json/test.bjson differ
diff --git a/tests/auto/json/test.json b/tests/auto/json/test.json
new file mode 100644
index 0000000000000000000000000000000000000000..330756894a6933dcf57a120d6362c2a71a10143d
--- /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 0000000000000000000000000000000000000000..303f879b62674ee6f6cbb87d80cd4602a0232e25
--- /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 0000000000000000000000000000000000000000..48cb29a47f124f2507ffbb7c000632a85b3ea42e
--- /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 0000000000000000000000000000000000000000..f24c09710deb7864f1c31ce23cc69acb1c9e891d
--- /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 0000000000000000000000000000000000000000..935696826591c5a313499467639ef5428dd0fffb
--- /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 0000000000000000000000000000000000000000..469156a78a3d8e30c336cd73d89c912a8b56c0fc
--- /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 0000000000000000000000000000000000000000..330756894a6933dcf57a120d6362c2a71a10143d
--- /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 0000000000000000000000000000000000000000..024819506418c4b922747c4c37d25b2924701b80
--- /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"
+