blob: 57aa67c1428a6617eb00fde4cc8db00ac7a9b159 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest>
#include "qjsonarray.h"
#include "qjsonobject.h"
#include "qjsonvalue.h"
#include "qjsondocument.h"
#include "qregularexpression.h"
#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
class tst_QtJson: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testValueSimple();
void testNumbers();
void testNumbers_2();
void testNumbers_3();
void testNumbers_4();
void testObjectSimple();
void testObjectSmallKeys();
void testArraySimple();
void testValueObject();
void testValueArray();
void testObjectNested();
void testArrayNested();
void testArrayNestedEmpty();
void testArrayComfortOperators();
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 fromVariant_data();
void fromVariant();
void fromVariantSpecial_data();
void fromVariantSpecial();
void toVariant_data();
void toVariant();
void fromVariantMap();
void fromVariantHash();
void toVariantMap();
void toVariantHash();
void toVariantList();
void toJson();
void toJsonSillyNumericValues();
void toJsonLargeNumericValues();
void fromJson();
void fromJsonErrors();
void fromBinary();
void toAndFromBinary_data();
void toAndFromBinary();
void invalidBinaryData();
void parseNumbers();
void parseStrings();
void parseDuplicateKeys();
void testParser();
void compactArray();
void compactObject();
void validation();
void assignToDocument();
void testDuplicateKeys();
void testCompaction();
void testDebugStream();
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();
void documentFromVariant();
void parseErrorOffset_data();
void parseErrorOffset();
void implicitValueType();
void implicitDocumentType();
void streamSerializationQJsonDocument_data();
void streamSerializationQJsonDocument();
void streamSerializationQJsonArray_data();
void streamSerializationQJsonArray();
void streamSerializationQJsonObject_data();
void streamSerializationQJsonObject();
void streamSerializationQJsonValue_data();
void streamSerializationQJsonValue();
void streamSerializationQJsonValueEmpty();
void streamVariantSerialization();
void escapeSurrogateCodePoints_data();
void escapeSurrogateCodePoints();
private:
QString testDataDir;
};
void tst_QtJson::initTestCase()
{
testDataDir = QFileInfo(QFINDTESTDATA("test.json")).absolutePath();
if (testDataDir.isEmpty())
testDataDir = QCoreApplication::applicationDirPath();
}
void tst_QtJson::testValueSimple()
{
QJsonObject object;
object.insert("number", 999.);
QJsonArray array;
for (int i = 0; i < 10; ++i)
array.append((double)i);
QJsonValue value(true);
QCOMPARE(value.type(), QJsonValue::Bool);
QCOMPARE(value.toDouble(), 0.);
QCOMPARE(value.toString(), QString());
QCOMPARE(value.toBool(), true);
QCOMPARE(value.toObject(), QJsonObject());
QCOMPARE(value.toArray(), QJsonArray());
QCOMPARE(value.toDouble(99.), 99.);
QCOMPARE(value.toString(QString("test")), QString("test"));
QCOMPARE(value.toObject(object), object);
QCOMPARE(value.toArray(array), array);
value = 999.;
QCOMPARE(value.type(), QJsonValue::Double);
QCOMPARE(value.toDouble(), 999.);
QCOMPARE(value.toString(), QString());
QCOMPARE(value.toBool(), false);
QCOMPARE(value.toBool(true), true);
QCOMPARE(value.toObject(), QJsonObject());
QCOMPARE(value.toArray(), QJsonArray());
value = QLatin1String("test");
QCOMPARE(value.toDouble(), 0.);
QCOMPARE(value.toString(), QLatin1String("test"));
QCOMPARE(value.toBool(), false);
QCOMPARE(value.toObject(), QJsonObject());
QCOMPARE(value.toArray(), QJsonArray());
}
void tst_QtJson::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);
QJsonArray array;
for (int i = 0; i < n; ++i)
array.append((double)numbers[i]);
QByteArray serialized = QJsonDocument(array).toJson();
QJsonDocument json = QJsonDocument::fromJson(serialized);
QJsonArray array2 = json.array();
QCOMPARE(array.size(), array2.size());
for (int i = 0; i < array.size(); ++i) {
QCOMPARE(array.at(i).type(), QJsonValue::Double);
QCOMPARE(array.at(i).toDouble(), (double)numbers[i]);
QCOMPARE(array2.at(i).type(), QJsonValue::Double);
QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]);
}
}
{
qint64 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(qint64);
QJsonArray array;
for (int i = 0; i < n; ++i)
array.append((double)numbers[i]);
QByteArray serialized = QJsonDocument(array).toJson();
QJsonDocument json = QJsonDocument::fromJson(serialized);
QJsonArray array2 = json.array();
QCOMPARE(array.size(), array2.size());
for (int i = 0; i < array.size(); ++i) {
QCOMPARE(array.at(i).type(), QJsonValue::Double);
QCOMPARE(array.at(i).toDouble(), (double)numbers[i]);
QCOMPARE(array2.at(i).type(), QJsonValue::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);
QJsonArray array;
for (int i = 0; i < n; ++i)
array.append(numbers[i]);
QByteArray serialized = QJsonDocument(array).toJson();
QJsonDocument json = QJsonDocument::fromJson(serialized);
QJsonArray array2 = json.array();
QCOMPARE(array.size(), array2.size());
for (int i = 0; i < array.size(); ++i) {
QCOMPARE(array.at(i).type(), QJsonValue::Double);
QCOMPARE(array.at(i).toDouble(), numbers[i]);
QCOMPARE(array2.at(i).type(), QJsonValue::Double);
QCOMPARE(array2.at(i).toDouble(), numbers[i]);
}
}
}
void tst_QtJson::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];
QJsonObject jObject;
for (int power = 0; power <= 1075; power++) {
floatValues[power] = value;
jObject.insert(QString::number(power), QJsonValue(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;
}
QJsonDocument jDocument1(jObject);
QByteArray ba(jDocument1.toJson());
QJsonDocument jDocument2(QJsonDocument::fromJson(ba));
for (int power = 0; power <= 1075; power++) {
floatValues_1[power] = jDocument2.object().value(QString::number(power)).toDouble();
#ifdef Q_OS_QNX
if (power >= 970)
QEXPECT_FAIL("", "See QTBUG-37066", Abort);
#endif
QVERIFY2(floatValues[power] == floatValues_1[power], QString("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("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("2**- %1 should not be 0").arg(index).toLatin1());
QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1").arg(index).toLatin1());
}
}
void tst_QtJson::testNumbers_3()
{
// test case from QTBUG-31926
double d1 = 1.123451234512345;
double d2 = 1.123451234512346;
QJsonObject jObject;
jObject.insert("d1", QJsonValue(d1));
jObject.insert("d2", QJsonValue(d2));
QJsonDocument jDocument1(jObject);
QByteArray ba(jDocument1.toJson());
QJsonDocument jDocument2(QJsonDocument::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_QtJson::testNumbers_4()
{
// no exponent notation used to print numbers between -2^64 and 2^64
QJsonArray array;
array << QJsonValue(+1000000000000000.0);
array << QJsonValue(-1000000000000000.0);
array << QJsonValue(+9007199254740992.0);
array << QJsonValue(-9007199254740992.0);
array << QJsonValue(+9223372036854775808.0);
array << QJsonValue(-9223372036854775808.0);
array << QJsonValue(+18446744073709551616.0);
array << QJsonValue(-18446744073709551616.0);
const QByteArray json(QJsonDocument(array).toJson());
const QByteArray expected =
"[\n"
" 1000000000000000,\n"
" -1000000000000000,\n"
" 9007199254740992,\n"
" -9007199254740992,\n"
" 9223372036854776000,\n"
" -9223372036854776000,\n"
" 18446744073709552000,\n"
" -18446744073709552000\n"
"]\n";
QCOMPARE(json, expected);
}
void tst_QtJson::testObjectSimple()
{
QJsonObject object;
object.insert("number", 999.);
QCOMPARE(object.value("number").type(), QJsonValue::Double);
QCOMPARE(object.value(QLatin1String("number")).toDouble(), 999.);
object.insert("string", QString::fromLatin1("test"));
QCOMPARE(object.value("string").type(), QJsonValue::String);
QCOMPARE(object.value(QLatin1String("string")).toString(), QString("test"));
object.insert("boolean", true);
QCOMPARE(object.value("boolean").toBool(), true);
QCOMPARE(object.value(QLatin1String("boolean")).toBool(), true);
QJsonObject object2 = object;
QJsonObject object3 = object;
QStringList keys = object.keys();
QVERIFY2(keys.contains("number"), "key number not found");
QVERIFY2(keys.contains("string"), "key string not found");
QVERIFY2(keys.contains("boolean"), "key boolean not found");
// if we put a JsonValue into the JsonObject and retrieve
// it, it should be identical.
QJsonValue value(QLatin1String("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");
QJsonValue taken = object.take("value");
QCOMPARE(taken, value);
QVERIFY2(!object.contains("value"), "key value should have been removed");
QString before = object.value("string").toString();
object.insert("string", QString::fromLatin1("foo"));
QVERIFY2(object.value(QLatin1String("string")).toString() != before, "value should have been updated");
// same tests again but with QStringView keys
object2.insert(QStringView(u"value"), value);
QCOMPARE(object2.value("value"), value);
size = object2.size();
object2.remove(QStringView(u"boolean"));
QCOMPARE(object2.size(), size - 1);
QVERIFY2(!object2.contains(QStringView(u"boolean")), "key boolean should have been removed");
taken = object2.take(QStringView(u"value"));
QCOMPARE(taken, value);
QVERIFY2(!object2.contains("value"), "key value should have been removed");
before = object2.value("string").toString();
object2.insert(QStringView(u"string"), QString::fromLatin1("foo"));
QVERIFY2(object2.value(QStringView(u"string")).toString() != before, "value should have been updated");
// same tests again but with QLatin1String keys
object3.insert(QLatin1String("value"), value);
QCOMPARE(object3.value("value"), value);
size = object3.size();
object3.remove(QLatin1String("boolean"));
QCOMPARE(object3.size(), size - 1);
QVERIFY2(!object3.contains("boolean"), "key boolean should have been removed");
taken = object3.take(QLatin1String("value"));
QCOMPARE(taken, value);
QVERIFY2(!object3.contains("value"), "key value should have been removed");
before = object3.value("string").toString();
object3.insert(QLatin1String("string"), QString::fromLatin1("foo"));
QVERIFY2(object3.value(QLatin1String("string")).toString() != before, "value should have been updated");
size = object.size();
QJsonObject subobject;
subobject.insert("number", 42);
subobject.insert(QLatin1String("string"), QLatin1String("foobar"));
object.insert("subobject", subobject);
QCOMPARE(object.size(), size+1);
QJsonValue subvalue = object.take(QLatin1String("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(QLatin1String("string"), QLatin1String("bar"));
QCOMPARE(object.size(), size);
QCOMPARE(subvalue.toObject(), subobject);
}
void tst_QtJson::testObjectSmallKeys()
{
QJsonObject data1;
data1.insert(QStringLiteral("1"), 123.);
QVERIFY(data1.contains(QStringLiteral("1")));
QCOMPARE(data1.value(QStringLiteral("1")).toDouble(), (double)123);
data1.insert(QStringLiteral("12"), 133.);
QCOMPARE(data1.value(QStringLiteral("12")).toDouble(), (double)133);
QVERIFY(data1.contains(QStringLiteral("12")));
data1.insert(QStringLiteral("123"), 323.);
QCOMPARE(data1.value(QStringLiteral("12")).toDouble(), (double)133);
QVERIFY(data1.contains(QStringLiteral("123")));
QCOMPARE(data1.value(QStringLiteral("123")).type(), QJsonValue::Double);
QCOMPARE(data1.value(QStringLiteral("123")).toDouble(), (double)323);
}
void tst_QtJson::testArraySimple()
{
QJsonArray array;
array.append(999.);
array.append(QString::fromLatin1("test"));
array.append(true);
QJsonValue val = array.at(0);
QCOMPARE(array.at(0).toDouble(), 999.);
QCOMPARE(array.at(1).toString(), QString("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.
QJsonValue value(QLatin1String("foo"));
array.append(value);
QCOMPARE(array.at(3), value);
int size = array.size();
array.removeAt(2);
--size;
QCOMPARE(array.size(), size);
QJsonValue taken = array.takeAt(0);
--size;
QCOMPARE(taken.toDouble(), 999.);
QCOMPARE(array.size(), size);
// check whether null values work
array.append(QJsonValue());
++size;
QCOMPARE(array.size(), size);
QCOMPARE(array.last().type(), QJsonValue::Null);
QCOMPARE(array.last(), QJsonValue());
QCOMPARE(array.first().type(), QJsonValue::String);
QCOMPARE(array.first(), QJsonValue(QLatin1String("test")));
array.prepend(false);
QCOMPARE(array.first().type(), QJsonValue::Bool);
QCOMPARE(array.first(), QJsonValue(false));
QCOMPARE(array.at(-1), QJsonValue(QJsonValue::Undefined));
QCOMPARE(array.at(array.size()), QJsonValue(QJsonValue::Undefined));
array.replace(0, -555.);
QCOMPARE(array.first().type(), QJsonValue::Double);
QCOMPARE(array.first(), QJsonValue(-555.));
QCOMPARE(array.at(1).type(), QJsonValue::String);
QCOMPARE(array.at(1), QJsonValue(QLatin1String("test")));
}
void tst_QtJson::testValueObject()
{
QJsonObject object;
object.insert("number", 999.);
object.insert("string", QLatin1String("test"));
object.insert("boolean", true);
QJsonValue 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", QJsonValue(QLatin1String("test")));
QVERIFY2(value.toObject() != object, "object should have detached");
}
void tst_QtJson::testValueArray()
{
QJsonArray array;
array.append(999.);
array.append(QLatin1String("test"));
array.append(true);
QJsonValue 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(QLatin1String("test"));
QVERIFY2(value.toArray() != array, "array should have detached");
}
void tst_QtJson::testObjectNested()
{
QJsonObject 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).
QJsonObject 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
QJsonArray 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
QJsonObject 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_QtJson::testArrayNested()
{
QJsonArray inner, outer;
inner.append(999.);
outer.append(inner);
// if we don't modify the original JsonArray, value()
// should return the same array (non-detached).
QJsonArray 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
QJsonObject object;
object.insert("boolean", true);
outer.append(object);
QCOMPARE(outer.last().toObject(), object);
QCOMPARE(outer.last().toObject().value("boolean").toBool(), true);
// two deep arrays
QJsonArray twoDeep;
twoDeep.append(QJsonValue(QString::fromLatin1("nested")));
inner.append(twoDeep);
outer.append(inner);
QCOMPARE(outer.last().toArray().last().toArray(), twoDeep);
QCOMPARE(outer.last().toArray().last().toArray().at(0).toString(), QString("nested"));
}
void tst_QtJson::testArrayNestedEmpty()
{
QJsonObject object;
QJsonArray inner;
object.insert("inner", inner);
QJsonValue val = object.value("inner");
QJsonArray value = object.value("inner").toArray();
QVERIFY(QJsonDocument(value).isArray());
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());
QJsonDocument(object).toBinaryData();
QCOMPARE(object.value("inner").toArray().size(), 0);
}
void tst_QtJson::testObjectNestedEmpty()
{
QJsonObject object;
QJsonObject inner;
QJsonObject inner2;
object.insert("inner", inner);
object.insert("inner2", inner2);
QJsonObject value = object.value("inner").toObject();
QVERIFY(QJsonDocument(value).isObject());
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(), QJsonValue::Object);
QJsonDocument(object).toBinaryData();
QVERIFY(object.value("inner").toObject().isEmpty());
QVERIFY(object.value("inner2").toObject().isEmpty());
QJsonDocument doc = QJsonDocument::fromBinaryData(QJsonDocument(object).toBinaryData());
QVERIFY(!doc.isNull());
QJsonObject reconstituted(doc.object());
QCOMPARE(reconstituted.value("inner").toObject().size(), 0);
QCOMPARE(reconstituted.value("inner").type(), QJsonValue::Object);
QCOMPARE(reconstituted.value("inner2").type(), QJsonValue::Object);
}
void tst_QtJson::testArrayComfortOperators()
{
QJsonArray first;
first.append(123.);
first.append(QLatin1String("foo"));
QJsonArray second = QJsonArray() << 123. << QLatin1String("foo");
QCOMPARE(first, second);
first = first + QLatin1String("bar");
second += QLatin1String("bar");
QCOMPARE(first, second);
}
void tst_QtJson::testValueRef()
{
QJsonArray 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(), QJsonValue::Bool);
QCOMPARE(array.at(1).toBool(), false);
QJsonObject object;
object[QLatin1String("key")] = true;
QCOMPARE(object.size(), 1);
object.insert(QLatin1String("null"), QJsonValue());
QCOMPARE(object.value(QLatin1String("null")), QJsonValue());
object[QLatin1String("null")] = 100.;
QCOMPARE(object.value(QLatin1String("null")).type(), QJsonValue::Double);
QJsonValue val = qAsConst(object)[QLatin1String("null")];
QCOMPARE(val.toDouble(), 100.);
QCOMPARE(object.size(), 2);
array[1] = array[2] = object[QLatin1String("key")] = 42;
QCOMPARE(array[1], array[2]);
QCOMPARE(array[2], object[QLatin1String("key")]);
QCOMPARE(object.value(QLatin1String("key")), QJsonValue(42));
}
void tst_QtJson::testObjectIteration()
{
QJsonObject object;
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it)
QVERIFY(false);
const QString property = "kkk";
object.insert(property, 11);
object.take(property);
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it)
QVERIFY(false);
for (int i = 0; i < 10; ++i)
object[QString::number(i)] = (double)i;
QCOMPARE(object.size(), 10);
QCOMPARE(object.begin()->toDouble(), object.constBegin()->toDouble());
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) {
QJsonValue value = it.value();
QCOMPARE((double)it.key().toInt(), value.toDouble());
}
{
QJsonObject object2 = object;
QCOMPARE(object, object2);
QJsonValue val = *object2.begin();
object2.erase(object2.begin());
QCOMPARE(object.size(), 10);
QCOMPARE(object2.size(), 9);
for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) {
QJsonValue value = it.value();
QVERIFY(it.value() != val);
QCOMPARE((double)it.key().toInt(), value.toDouble());
}
}
{
QJsonObject object2 = object;
QCOMPARE(object, object2);
QJsonObject::iterator it = object2.find(QString::number(5));
object2.erase(it);
QCOMPARE(object.size(), 10);
QCOMPARE(object2.size(), 9);
}
{
QJsonObject::Iterator it = object.begin();
it += 5;
QCOMPARE(QJsonValue(it.value()).toDouble(), 5.);
it -= 3;
QCOMPARE(QJsonValue(it.value()).toDouble(), 2.);
QJsonObject::Iterator it2 = it + 5;
QCOMPARE(QJsonValue(it2.value()).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(QJsonValue(it2.value()).toDouble(), 1.);
}
{
QJsonObject::ConstIterator it = object.constBegin();
it += 5;
QCOMPARE(QJsonValue(it.value()).toDouble(), 5.);
it -= 3;
QCOMPARE(QJsonValue(it.value()).toDouble(), 2.);
QJsonObject::ConstIterator it2 = it + 5;
QCOMPARE(QJsonValue(it2.value()).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(QJsonValue(it2.value()).toDouble(), 1.);
}
QJsonObject::Iterator it = object.begin();
while (!object.isEmpty())
it = object.erase(it);
QCOMPARE(object.size() , 0);
QCOMPARE(it, object.end());
}
void tst_QtJson::testArrayIteration()
{
QJsonArray array;
for (int i = 0; i < 10; ++i)
array.append(i);
QCOMPARE(array.size(), 10);
int i = 0;
for (QJsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) {
QJsonValue value = (*it);
QCOMPARE((double)i, value.toDouble());
}
QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble());
{
QJsonArray array2 = array;
QCOMPARE(array, array2);
QJsonValue val = *array2.begin();
array2.erase(array2.begin());
QCOMPARE(array.size(), 10);
QCOMPARE(array2.size(), 9);
i = 1;
for (QJsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) {
QJsonValue value = (*it);
QCOMPARE((double)i, value.toDouble());
}
}
{
QJsonArray::Iterator it = array.begin();
it += 5;
QCOMPARE(QJsonValue((*it)).toDouble(), 5.);
it -= 3;
QCOMPARE(QJsonValue((*it)).toDouble(), 2.);
QJsonArray::Iterator it2 = it + 5;
QCOMPARE(QJsonValue(*it2).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(QJsonValue(*it2).toDouble(), 1.);
}
{
QJsonArray::ConstIterator it = array.constBegin();
it += 5;
QCOMPARE(QJsonValue((*it)).toDouble(), 5.);
it -= 3;
QCOMPARE(QJsonValue((*it)).toDouble(), 2.);
QJsonArray::ConstIterator it2 = it + 5;
QCOMPARE(QJsonValue(*it2).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(QJsonValue(*it2).toDouble(), 1.);
}
QJsonArray::Iterator it = array.begin();
while (!array.isEmpty())
it = array.erase(it);
QCOMPARE(array.size() , 0);
QCOMPARE(it, array.end());
}
void tst_QtJson::testObjectFind()
{
QJsonObject object;
for (int i = 0; i < 10; ++i)
object[QString::number(i)] = i;
QCOMPARE(object.size(), 10);
QJsonObject::iterator it = object.find(QLatin1String("1"));
QCOMPARE((*it).toDouble(), 1.);
it = object.find(QString("11"));
QCOMPARE((*it).type(), QJsonValue::Undefined);
QCOMPARE(it, object.end());
QJsonObject::const_iterator cit = object.constFind(QLatin1String("1"));
QCOMPARE((*cit).toDouble(), 1.);
cit = object.constFind(QString("11"));
QCOMPARE((*it).type(), QJsonValue::Undefined);
QCOMPARE(it, object.end());
}
void tst_QtJson::testDocument()
{
QJsonDocument doc;
QCOMPARE(doc.isEmpty(), true);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), false);
QJsonObject object;
doc.setObject(object);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
object.insert(QLatin1String("Key"), QLatin1String("Value"));
doc.setObject(object);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QCOMPARE(doc.object(), object);
QCOMPARE(doc.array(), QJsonArray());
doc = QJsonDocument();
QCOMPARE(doc.isEmpty(), true);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), false);
QJsonArray array;
doc.setArray(array);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
array.append(QLatin1String("Value"));
doc.setArray(array);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QCOMPARE(doc.array(), array);
QCOMPARE(doc.object(), QJsonObject());
QJsonObject outer;
outer.insert(QLatin1String("outerKey"), 22);
QJsonObject inner;
inner.insert(QLatin1String("innerKey"), 42);
outer.insert(QLatin1String("innter"), inner);
QJsonArray innerArray;
innerArray.append(23);
outer.insert(QLatin1String("innterArray"), innerArray);
QJsonDocument doc2(outer.value(QLatin1String("innter")).toObject());
QVERIFY(doc2.object().contains(QLatin1String("innerKey")));
QCOMPARE(doc2.object().value(QLatin1String("innerKey")), QJsonValue(42));
QJsonDocument doc3;
doc3.setObject(outer.value(QLatin1String("innter")).toObject());
QCOMPARE(doc3.isArray(), false);
QCOMPARE(doc3.isObject(), true);
QVERIFY(doc3.object().contains(QString("innerKey")));
QCOMPARE(doc3.object().value(QLatin1String("innerKey")), QJsonValue(42));
QJsonDocument doc4(outer.value(QLatin1String("innterArray")).toArray());
QCOMPARE(doc4.isArray(), true);
QCOMPARE(doc4.isObject(), false);
QCOMPARE(doc4.array().size(), 1);
QCOMPARE(doc4.array().at(0), QJsonValue(23));
QJsonDocument doc5;
doc5.setArray(outer.value(QLatin1String("innterArray")).toArray());
QCOMPARE(doc5.isArray(), true);
QCOMPARE(doc5.isObject(), false);
QCOMPARE(doc5.array().size(), 1);
QCOMPARE(doc5.array().at(0), QJsonValue(23));
}
void tst_QtJson::nullValues()
{
QJsonArray array;
array.append(QJsonValue());
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0), QJsonValue());
QJsonObject object;
object.insert(QString("key"), QJsonValue());
QCOMPARE(object.contains(QLatin1String("key")), true);
QCOMPARE(object.size(), 1);
QCOMPARE(object.value(QString("key")), QJsonValue());
}
void tst_QtJson::nullArrays()
{
QJsonArray nullArray;
QJsonArray nonNull;
nonNull.append(QLatin1String("bar"));
QCOMPARE(nullArray, QJsonArray());
QVERIFY(nullArray != nonNull);
QVERIFY(nonNull != nullArray);
QCOMPARE(nullArray.size(), 0);
QCOMPARE(nullArray.takeAt(0), QJsonValue(QJsonValue::Undefined));
QCOMPARE(nullArray.first(), QJsonValue(QJsonValue::Undefined));
QCOMPARE(nullArray.last(), QJsonValue(QJsonValue::Undefined));
nullArray.removeAt(0);
nullArray.removeAt(-1);
nullArray.append(QString("bar"));
nullArray.removeAt(0);
QCOMPARE(nullArray.size(), 0);
QCOMPARE(nullArray.takeAt(0), QJsonValue(QJsonValue::Undefined));
QCOMPARE(nullArray.first(), QJsonValue(QJsonValue::Undefined));
QCOMPARE(nullArray.last(), QJsonValue(QJsonValue::Undefined));
nullArray.removeAt(0);
nullArray.removeAt(-1);
}
void tst_QtJson::nullObject()
{
QJsonObject nullObject;
QJsonObject nonNull;
nonNull.insert(QLatin1String("foo"), QLatin1String("bar"));
QCOMPARE(nullObject, QJsonObject());
QVERIFY(nullObject != nonNull);
QVERIFY(nonNull != nullObject);
QCOMPARE(nullObject.size(), 0);
QCOMPARE(nullObject.keys(), QStringList());
nullObject.remove("foo");
QCOMPARE(nullObject, QJsonObject());
QCOMPARE(nullObject.take("foo"), QJsonValue(QJsonValue::Undefined));
QCOMPARE(nullObject.contains("foo"), false);
nullObject.insert("foo", QString("bar"));
nullObject.remove("foo");
QCOMPARE(nullObject.size(), 0);
QCOMPARE(nullObject.keys(), QStringList());
nullObject.remove("foo");
QCOMPARE(nullObject, QJsonObject());
QCOMPARE(nullObject.take("foo"), QJsonValue(QJsonValue::Undefined));
QCOMPARE(nullObject.contains("foo"), false);
}
void tst_QtJson::constNullObject()
{
const QJsonObject nullObject;
QJsonObject nonNull;
nonNull.insert(QLatin1String("foo"), QLatin1String("bar"));
QCOMPARE(nullObject, QJsonObject());
QVERIFY(nullObject != nonNull);
QVERIFY(nonNull != nullObject);
QCOMPARE(nullObject.size(), 0);
QCOMPARE(nullObject.keys(), QStringList());
QCOMPARE(nullObject, QJsonObject());
QCOMPARE(nullObject.contains("foo"), false);
QCOMPARE(nullObject["foo"], QJsonValue(QJsonValue::Undefined));
}
void tst_QtJson::keySorting()
{
const char *json = "{ \"B\": true, \"A\": false }";
QJsonDocument doc = QJsonDocument::fromJson(json);
QCOMPARE(doc.isObject(), true);
QJsonObject o = doc.object();
QCOMPARE(o.size(), 2);
QJsonObject::const_iterator it = o.constBegin();
QCOMPARE(it.key(), QLatin1String("A"));
++it;
QCOMPARE(it.key(), QLatin1String("B"));
QCOMPARE(o.keys(), QStringList() << QLatin1String("A") << QLatin1String("B"));
}
void tst_QtJson::undefinedValues()
{
QJsonObject object;
object.insert("Key", QJsonValue(QJsonValue::Undefined));
QCOMPARE(object.size(), 0);
object["Key"] = QJsonValue(QJsonValue::Undefined);
QCOMPARE(object.size(), 0);
object.insert("Key", QLatin1String("Value"));
QCOMPARE(object.size(), 1);
QCOMPARE(object.value("Key").type(), QJsonValue::String);
QCOMPARE(object.value("foo").type(), QJsonValue::Undefined);
object.insert("Key", QJsonValue(QJsonValue::Undefined));
QCOMPARE(object.size(), 0);
QCOMPARE(object.value("Key").type(), QJsonValue::Undefined);
QJsonArray array;
array.append(QJsonValue(QJsonValue::Undefined));
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0).type(), QJsonValue::Null);
QCOMPARE(array.at(1).type(), QJsonValue::Undefined);
QCOMPARE(array.at(-1).type(), QJsonValue::Undefined);
}
void tst_QtJson::fromVariant_data()
{
QTest::addColumn<QVariant>("variant");
QTest::addColumn<QJsonValue>("jsonvalue");
bool boolValue = true;
int intValue = -1;
uint uintValue = 1;
long long longlongValue = -2;
unsigned long long ulonglongValue = 2;
float floatValue = 3.3f;
double doubleValue = 4.4;
QString stringValue("str");
QStringList stringList;
stringList.append(stringValue);
stringList.append("str2");
QJsonArray jsonArray_string;
jsonArray_string.append(stringValue);
jsonArray_string.append("str2");
QVariantList variantList;
variantList.append(boolValue);
variantList.append(floatValue);
variantList.append(doubleValue);
variantList.append(stringValue);
variantList.append(stringList);
variantList.append(QVariant::fromValue(nullptr));
QJsonArray jsonArray_variant;
jsonArray_variant.append(boolValue);
jsonArray_variant.append(floatValue);
jsonArray_variant.append(doubleValue);
jsonArray_variant.append(stringValue);
jsonArray_variant.append(jsonArray_string);
jsonArray_variant.append(QJsonValue(QJsonValue::Null));
QVariantMap variantMap;
variantMap["bool"] = boolValue;
variantMap["float"] = floatValue;
variantMap["string"] = stringValue;
variantMap["array"] = variantList;
QVariantHash variantHash;
variantHash["bool"] = boolValue;
variantHash["float"] = floatValue;
variantHash["string"] = stringValue;
variantHash["array"] = variantList;
QJsonObject jsonObject;
jsonObject["bool"] = boolValue;
jsonObject["float"] = floatValue;
jsonObject["string"] = stringValue;
jsonObject["array"] = jsonArray_variant;
QTest::newRow("nullptr") << QVariant::fromValue(nullptr) << QJsonValue(QJsonValue::Null);
QTest::newRow("bool") << QVariant(boolValue) << QJsonValue(boolValue);
QTest::newRow("int") << QVariant(intValue) << QJsonValue(intValue);
QTest::newRow("uint") << QVariant(uintValue) << QJsonValue(static_cast<double>(uintValue));
QTest::newRow("longlong") << QVariant(longlongValue) << QJsonValue(longlongValue);
QTest::newRow("ulonglong") << QVariant(ulonglongValue) << QJsonValue(static_cast<double>(ulonglongValue));
QTest::newRow("float") << QVariant(floatValue) << QJsonValue(floatValue);
QTest::newRow("double") << QVariant(doubleValue) << QJsonValue(doubleValue);
QTest::newRow("string") << QVariant(stringValue) << QJsonValue(stringValue);
QTest::newRow("stringList") << QVariant(stringList) << QJsonValue(jsonArray_string);
QTest::newRow("variantList") << QVariant(variantList) << QJsonValue(jsonArray_variant);
QTest::newRow("variantMap") << QVariant(variantMap) << QJsonValue(jsonObject);
QTest::newRow("variantHash") << QVariant(variantHash) << QJsonValue(jsonObject);
}
void tst_QtJson::fromVariant()
{
QFETCH( QVariant, variant );
QFETCH( QJsonValue, jsonvalue );
QCOMPARE(QJsonValue::fromVariant(variant), jsonvalue);
QCOMPARE(variant.toJsonValue(), jsonvalue);
}
void tst_QtJson::fromVariantSpecial_data()
{
QTest::addColumn<QVariant>("variant");
QTest::addColumn<QJsonValue>("jsonvalue");
// Qt types with special encoding
QTest::newRow("url") << QVariant(QUrl("https://example.com/\xc2\xa9 "))
<< QJsonValue("https://example.com/%C2%A9%20");
QTest::newRow("uuid") << QVariant(QUuid(0x40c01df6, 0x1ad5, 0x4762, 0x9c, 0xfe, 0xfd, 0xba, 0xfa, 0xb5, 0xde, 0xf8))
<< QJsonValue("40c01df6-1ad5-4762-9cfe-fdbafab5def8");
}
void tst_QtJson::fromVariantSpecial()
{
QFETCH( QVariant, variant );
QFETCH( QJsonValue, jsonvalue );
QCOMPARE(QJsonValue::fromVariant(variant), jsonvalue);
}
void tst_QtJson::toVariant_data()
{
fromVariant_data();
}
void tst_QtJson::toVariant()
{
QFETCH( QVariant, variant );
QFETCH( QJsonValue, jsonvalue );
QCOMPARE(jsonvalue.toVariant(), variant);
}
void tst_QtJson::fromVariantMap()
{
QVariantMap map;
map.insert(QLatin1String("key1"), QLatin1String("value1"));
map.insert(QLatin1String("key2"), QLatin1String("value2"));
QJsonObject object = QJsonObject::fromVariantMap(map);
QCOMPARE(object.size(), 2);
QCOMPARE(object.value(QLatin1String("key1")), QJsonValue(QLatin1String("value1")));
QCOMPARE(object.value(QLatin1String("key2")), QJsonValue(QLatin1String("value2")));
QVariantList list;
list.append(true);
list.append(QVariant());
list.append(999.);
list.append(QLatin1String("foo"));
map.insert("list", list);
object = QJsonObject::fromVariantMap(map);
QCOMPARE(object.size(), 3);
QCOMPARE(object.value(QLatin1String("key1")), QJsonValue(QLatin1String("value1")));
QCOMPARE(object.value(QLatin1String("key2")), QJsonValue(QLatin1String("value2")));
QCOMPARE(object.value(QLatin1String("list")).type(), QJsonValue::Array);
QJsonArray array = object.value(QLatin1String("list")).toArray();
QCOMPARE(array.size(), 4);
QCOMPARE(array.at(0).type(), QJsonValue::Bool);
QCOMPARE(array.at(0).toBool(), true);
QCOMPARE(array.at(1).type(), QJsonValue::Null);
QCOMPARE(array.at(2).type(), QJsonValue::Double);
QCOMPARE(array.at(2).toDouble(), 999.);
QCOMPARE(array.at(3).type(), QJsonValue::String);
QCOMPARE(array.at(3).toString(), QLatin1String("foo"));
}
void tst_QtJson::fromVariantHash()
{
QVariantHash map;
map.insert(QLatin1String("key1"), QLatin1String("value1"));
map.insert(QLatin1String("key2"), QLatin1String("value2"));
QJsonObject object = QJsonObject::fromVariantHash(map);
QCOMPARE(object.size(), 2);
QCOMPARE(object.value(QLatin1String("key1")), QJsonValue(QLatin1String("value1")));
QCOMPARE(object.value(QLatin1String("key2")), QJsonValue(QLatin1String("value2")));
}
void tst_QtJson::toVariantMap()
{
QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().type()), QMetaType::QVariantMap); // QTBUG-32524
QJsonObject object;
QVariantMap map = object.toVariantMap();
QVERIFY(map.isEmpty());
object.insert("Key", QString("Value"));
object.insert("null", QJsonValue());
QJsonArray array;
array.append(true);
array.append(999.);
array.append(QLatin1String("string"));
array.append(QJsonValue::Null);
object.insert("Array", array);
map = object.toVariantMap();
QCOMPARE(map.size(), 3);
QCOMPARE(map.value("Key"), QVariant(QString("Value")));
QCOMPARE(map.value("null"), QVariant::fromValue(nullptr));
QCOMPARE(map.value("Array").type(), QVariant::List);
QVariantList list = map.value("Array").toList();
QCOMPARE(list.size(), 4);
QCOMPARE(list.at(0), QVariant(true));
QCOMPARE(list.at(1), QVariant(999.));
QCOMPARE(list.at(2), QVariant(QLatin1String("string")));
QCOMPARE(list.at(3), QVariant::fromValue(nullptr));
}
void tst_QtJson::toVariantHash()
{
QJsonObject object;
QVariantHash hash = object.toVariantHash();
QVERIFY(hash.isEmpty());
object.insert("Key", QString("Value"));
object.insert("null", QJsonValue::Null);
QJsonArray array;
array.append(true);
array.append(999.);
array.append(QLatin1String("string"));
array.append(QJsonValue::Null);
object.insert("Array", array);
hash = object.toVariantHash();
QCOMPARE(hash.size(), 3);
QCOMPARE(hash.value("Key"), QVariant(QString("Value")));
QCOMPARE(hash.value("null"), QVariant::fromValue(nullptr));
QCOMPARE(hash.value("Array").type(), QVariant::List);
QVariantList list = hash.value("Array").toList();
QCOMPARE(list.size(), 4);
QCOMPARE(list.at(0), QVariant(true));
QCOMPARE(list.at(1), QVariant(999.));
QCOMPARE(list.at(2), QVariant(QLatin1String("string")));
QCOMPARE(list.at(3), QVariant::fromValue(nullptr));
}
void tst_QtJson::toVariantList()
{
QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().type()), QMetaType::QVariantList); // QTBUG-32524
QJsonArray array;
QVariantList list = array.toVariantList();
QVERIFY(list.isEmpty());
array.append(QString("Value"));
array.append(QJsonValue());
QJsonArray inner;
inner.append(true);
inner.append(999.);
inner.append(QLatin1String("string"));
inner.append(QJsonValue());
array.append(inner);
list = array.toVariantList();
QCOMPARE(list.size(), 3);
QCOMPARE(list[0], QVariant(QString("Value")));
QCOMPARE(list[1], QVariant::fromValue(nullptr));
QCOMPARE(list[2].type(), QVariant::List);
QVariantList vlist = list[2].toList();
QCOMPARE(vlist.size(), 4);
QCOMPARE(vlist.at(0), QVariant(true));
QCOMPARE(vlist.at(1), QVariant(999.));
QCOMPARE(vlist.at(2), QVariant(QLatin1String("string")));
QCOMPARE(vlist.at(3), QVariant::fromValue(nullptr));
}
void tst_QtJson::toJson()
{
// Test QJsonDocument::Indented format
{
QJsonObject object;
object.insert("\\Key\n", QString("Value"));
object.insert("null", QJsonValue());
QJsonArray array;
array.append(true);
array.append(999.);
array.append(QLatin1String("string"));
array.append(QJsonValue());
array.append(QLatin1String("\\\a\n\r\b\tabcABC\""));
object.insert("Array", array);
QByteArray json = QJsonDocument(object).toJson();
QByteArray 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);
QJsonDocument 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 QJsonDocument::Compact format
{
QJsonObject object;
object.insert("\\Key\n", QString("Value"));
object.insert("null", QJsonValue());
QJsonArray array;
array.append(true);
array.append(999.);
array.append(QLatin1String("string"));
array.append(QJsonValue());
array.append(QLatin1String("\\\a\n\r\b\tabcABC\""));
object.insert("Array", array);
QByteArray json = QJsonDocument(object).toJson(QJsonDocument::Compact);
QByteArray expected =
"{\"Array\":[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\":\"Value\",\"null\":null}";
QCOMPARE(json, expected);
QJsonDocument doc;
doc.setObject(object);
json = doc.toJson(QJsonDocument::Compact);
QCOMPARE(json, expected);
doc.setArray(array);
json = doc.toJson(QJsonDocument::Compact);
expected = "[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"]";
QCOMPARE(json, expected);
}
}
void tst_QtJson::toJsonSillyNumericValues()
{
QJsonObject object;
QJsonArray array;
array.append(QJsonValue(std::numeric_limits<double>::infinity())); // encode to: null
array.append(QJsonValue(-std::numeric_limits<double>::infinity())); // encode to: null
array.append(QJsonValue(std::numeric_limits<double>::quiet_NaN())); // encode to: null
object.insert("Array", array);
QByteArray json = QJsonDocument(object).toJson();
QByteArray expected =
"{\n"
" \"Array\": [\n"
" null,\n"
" null,\n"
" null\n"
" ]\n"
"}\n";
QCOMPARE(json, expected);
QJsonDocument doc;
doc.setObject(object);
json = doc.toJson();
QCOMPARE(json, expected);
}
void tst_QtJson::toJsonLargeNumericValues()
{
QJsonObject object;
QJsonArray array;
array.append(QJsonValue(1.234567)); // actual precision bug in Qt 5.0.0
array.append(QJsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE
array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE
array.append(QJsonValue(std::numeric_limits<double>::min()));
array.append(QJsonValue(std::numeric_limits<double>::max()));
array.append(QJsonValue(std::numeric_limits<double>::epsilon()));
array.append(QJsonValue(std::numeric_limits<double>::denorm_min()));
array.append(QJsonValue(0.0));
array.append(QJsonValue(-std::numeric_limits<double>::min()));
array.append(QJsonValue(-std::numeric_limits<double>::max()));
array.append(QJsonValue(-std::numeric_limits<double>::epsilon()));
array.append(QJsonValue(-std::numeric_limits<double>::denorm_min()));
array.append(QJsonValue(-0.0));
array.append(QJsonValue(9007199254740992LL)); // JS Number max integer
array.append(QJsonValue(-9007199254740992LL)); // JS Number min integer
object.insert("Array", array);
QByteArray json = QJsonDocument(object).toJson();
QByteArray expected =
"{\n"
" \"Array\": [\n"
" 1.234567,\n"
" 1.7976931348623157e+308,\n"
#ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then
" 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"
#else
" 5e-324,\n"
" 2.2250738585072014e-308,\n"
" 1.7976931348623157e+308,\n"
" 2.220446049250313e-16,\n"
" 5e-324,\n"
" 0,\n"
" -2.2250738585072014e-308,\n"
" -1.7976931348623157e+308,\n"
" -2.220446049250313e-16,\n"
" -5e-324,\n"
#endif
" 0,\n"
" 9007199254740992,\n"
" -9007199254740992\n"
" ]\n"
"}\n";
#ifdef Q_OS_QNX
QEXPECT_FAIL("", "See QTBUG-37066", Continue);
#endif
QCOMPARE(json, expected);
QJsonDocument doc;
doc.setObject(object);
json = doc.toJson();
#ifdef Q_OS_QNX
QEXPECT_FAIL("", "See QTBUG-37066", Continue);
#endif
QCOMPARE(json, expected);
}
void tst_QtJson::fromJson()
{
{
QByteArray json = "[\n true\n]\n";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0).type(), QJsonValue::Bool);
QCOMPARE(array.at(0).toBool(), true);
QCOMPARE(doc.toJson(), json);
}
{
//regression test: test if unicode_control_characters are correctly decoded
QByteArray json = "[\n \"" UNICODE_NON_CHARACTER "\"\n]\n";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0).type(), QJsonValue::String);
QCOMPARE(array.at(0).toString(), QString::fromUtf8(UNICODE_NON_CHARACTER));
QCOMPARE(doc.toJson(), json);
}
{
QByteArray json = "[]";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 0);
}
{
QByteArray json = "{}";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QJsonObject object = doc.object();
QCOMPARE(object.size(), 0);
}
{
QByteArray json = "{\n \"Key\": true\n}\n";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QJsonObject object = doc.object();
QCOMPARE(object.size(), 1);
QCOMPARE(object.value("Key"), QJsonValue(true));
QCOMPARE(doc.toJson(), json);
}
{
QByteArray json = "[ null, true, false, \"Foo\", 1, [], {} ]";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 7);
QCOMPARE(array.at(0).type(), QJsonValue::Null);
QCOMPARE(array.at(1).type(), QJsonValue::Bool);
QCOMPARE(array.at(1).toBool(), true);
QCOMPARE(array.at(2).type(), QJsonValue::Bool);
QCOMPARE(array.at(2).toBool(), false);
QCOMPARE(array.at(3).type(), QJsonValue::String);
QCOMPARE(array.at(3).toString(), QLatin1String("Foo"));
QCOMPARE(array.at(4).type(), QJsonValue::Double);
QCOMPARE(array.at(4).toDouble(), 1.);
QCOMPARE(array.at(5).type(), QJsonValue::Array);
QCOMPARE(array.at(5).toArray().size(), 0);
QCOMPARE(array.at(6).type(), QJsonValue::Object);
QCOMPARE(array.at(6).toObject().size(), 0);
}
{
QByteArray json = "{ \"0\": null, \"1\": true, \"2\": false, \"3\": \"Foo\", \"4\": 1, \"5\": [], \"6\": {} }";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QJsonObject object = doc.object();
QCOMPARE(object.size(), 7);
QCOMPARE(object.value("0").type(), QJsonValue::Null);
QCOMPARE(object.value("1").type(), QJsonValue::Bool);
QCOMPARE(object.value("1").toBool(), true);
QCOMPARE(object.value("2").type(), QJsonValue::Bool);
QCOMPARE(object.value("2").toBool(), false);
QCOMPARE(object.value("3").type(), QJsonValue::String);
QCOMPARE(object.value("3").toString(), QLatin1String("Foo"));
QCOMPARE(object.value("4").type(), QJsonValue::Double);
QCOMPARE(object.value("4").toDouble(), 1.);
QCOMPARE(object.value("5").type(), QJsonValue::Array);
QCOMPARE(object.value("5").toArray().size(), 0);
QCOMPARE(object.value("6").type(), QJsonValue::Object);
QCOMPARE(object.value("6").toObject().size(), 0);
}
{
QByteArray compactJson = "{\"Array\": [true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\": \"Value\",\"null\": null}";
QJsonDocument doc = QJsonDocument::fromJson(compactJson);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QJsonObject object = doc.object();
QCOMPARE(object.size(), 3);
QCOMPARE(object.value("\\Key\n").isString(), true);
QCOMPARE(object.value("\\Key\n").toString(), QString("Value"));
QCOMPARE(object.value("null").isNull(), true);
QCOMPARE(object.value("Array").isArray(), true);
QJsonArray 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(), QLatin1String("string"));
QCOMPARE(array.at(3).isNull(), true);
QCOMPARE(array.at(4).isString(), true);
QCOMPARE(array.at(4).toString(), QLatin1String("\\\a\n\r\b\tabcABC\""));
}
}
void tst_QtJson::fromJsonErrors()
{
{
QJsonParseError error;
QByteArray json = "{\n \n\n";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::UnterminatedObject);
QCOMPARE(error.offset, 8);
}
{
QJsonParseError error;
QByteArray json = "{\n \"key\" 10\n";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::MissingNameSeparator);
QCOMPARE(error.offset, 13);
}
{
QJsonParseError error;
QByteArray json = "[\n \n\n";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
QCOMPARE(error.offset, 8);
}
{
QJsonParseError error;
QByteArray json = "[\n 1, true\n\n";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
QCOMPARE(error.offset, 14);
}
{
QJsonParseError error;
QByteArray json = "[\n 1 true\n\n";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::MissingValueSeparator);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
QByteArray json = "[\n nul";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
QByteArray json = "[\n nulzz";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 10);
}
{
QJsonParseError error;
QByteArray json = "[\n tru";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
QByteArray json = "[\n trud]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 10);
}
{
QJsonParseError error;
QByteArray json = "[\n fal";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
QByteArray json = "[\n falsd]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 11);
}
{
QJsonParseError error;
QByteArray json = "[\n 11111";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::TerminationByNumber);
QCOMPARE(error.offset, 11);
}
{
QJsonParseError error;
QByteArray json = "[\n -1E10000]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalNumber);
QCOMPARE(error.offset, 14);
}
{
QJsonParseError error;
QByteArray json = "[\n -1e-10000]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalNumber);
QCOMPARE(error.offset, 15);
}
{
QJsonParseError error;
QByteArray json = "[\n \"\\u12\"]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalEscapeSequence);
QCOMPARE(error.offset, 11);
}
{
QJsonParseError error;
QByteArray json = "[\n \"foo" INVALID_UNICODE "bar\"]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalUTF8String);
QCOMPARE(error.offset, 12);
}
{
QJsonParseError error;
QByteArray json = "[\n \"";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::UnterminatedString);
QCOMPARE(error.offset, 8);
}
{
QJsonParseError error;
QByteArray json = "[\n \"c" UNICODE_DJE "a\\u12\"]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalEscapeSequence);
QCOMPARE(error.offset, 15);
}
{
QJsonParseError error;
QByteArray json = "[\n \"c" UNICODE_DJE "a" INVALID_UNICODE "bar\"]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::IllegalUTF8String);
QCOMPARE(error.offset, 13);
}
{
QJsonParseError error;
QByteArray json = "[\n \"c" UNICODE_DJE "a ]";
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, QJsonParseError::UnterminatedString);
QCOMPARE(error.offset, 14);
}
}
void tst_QtJson::fromBinary()
{
QFile file(testDataDir + "/test.json");
file.open(QFile::ReadOnly);
QByteArray testJson = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(testJson);
QJsonDocument outdoc = QJsonDocument::fromBinaryData(doc.toBinaryData());
QVERIFY(!outdoc.isNull());
QCOMPARE(doc, outdoc);
QFile bfile(testDataDir + "/test.bjson");
bfile.open(QFile::ReadOnly);
QByteArray binary = bfile.readAll();
QJsonDocument bdoc = QJsonDocument::fromBinaryData(binary);
QVERIFY(!bdoc.isNull());
QCOMPARE(doc.toVariant(), bdoc.toVariant());
QCOMPARE(doc, bdoc);
}
void tst_QtJson::toAndFromBinary_data()
{
QTest::addColumn<QString>("filename");
QTest::newRow("test.json") << (testDataDir + "/test.json");
QTest::newRow("test2.json") << (testDataDir + "/test2.json");
}
void tst_QtJson::toAndFromBinary()
{
QFETCH(QString, filename);
QFile file(filename);
QVERIFY(file.open(QFile::ReadOnly));
QByteArray data = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
QVERIFY(!doc.isNull());
QJsonDocument outdoc = QJsonDocument::fromBinaryData(doc.toBinaryData());
QVERIFY(!outdoc.isNull());
QCOMPARE(doc, outdoc);
}
void tst_QtJson::invalidBinaryData()
{
QDir dir(testDataDir + "/invalidBinaryData");
QFileInfoList files = dir.entryInfoList();
for (int i = 0; i < files.size(); ++i) {
if (!files.at(i).isFile())
continue;
QFile file(files.at(i).filePath());
file.open(QIODevice::ReadOnly);
QByteArray bytes = file.readAll();
bytes.squeeze();
QJsonDocument document = QJsonDocument::fromRawData(bytes.constData(), bytes.size());
QVERIFY(document.isNull());
}
}
void tst_QtJson::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) {
QByteArray json = "[ ";
json += numbers[i].str;
json += " ]";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QJsonValue val = array.at(0);
QCOMPARE(val.type(), QJsonValue::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) {
QByteArray json = "[ ";
json += numbers[i].str;
json += " ]";
QJsonDocument doc = QJsonDocument::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);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QJsonValue val = array.at(0);
QCOMPARE(val.type(), QJsonValue::Double);
QCOMPARE(val.toDouble(), numbers[i].n);
}
}
}
void tst_QtJson::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) {
QByteArray json = "[\n \"";
json += strings[i];
json += "\"\n]\n";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QJsonValue val = array.at(0);
QCOMPARE(val.type(), QJsonValue::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) {
QByteArray json = "[\n \"";
json += pairs[i].in;
json += "\"\n]\n";
QByteArray out = "[\n \"";
out += pairs[i].out;
out += "\"\n]\n";
QJsonDocument doc = QJsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QJsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QJsonValue val = array.at(0);
QCOMPARE(val.type(), QJsonValue::String);
QCOMPARE(doc.toJson(), out);
}
}
void tst_QtJson::parseDuplicateKeys()
{
const char *json = "{ \"B\": true, \"A\": null, \"B\": false }";
QJsonDocument doc = QJsonDocument::fromJson(json);
QCOMPARE(doc.isObject(), true);
QJsonObject o = doc.object();
QCOMPARE(o.size(), 2);
QJsonObject::const_iterator it = o.constBegin();
QCOMPARE(it.key(), QLatin1String("A"));
QCOMPARE(it.value(), QJsonValue());
++it;
QCOMPARE(it.key(), QLatin1String("B"));
QCOMPARE(it.value(), QJsonValue(false));
}
void tst_QtJson::testParser()
{
QFile file(testDataDir + "/test.json");
file.open(QFile::ReadOnly);
QByteArray testJson = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(testJson);
QVERIFY(!doc.isEmpty());
}
void tst_QtJson::compactArray()
{
QJsonArray array;
array.append(QLatin1String("First Entry"));
array.append(QLatin1String("Second Entry"));
array.append(QLatin1String("Third Entry"));
QJsonDocument doc(array);
int s = doc.toBinaryData().size();
array.removeAt(1);
doc.setArray(array);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
QByteArray("[\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(),
QByteArray("[\n"
" \"Third Entry\"\n"
"]\n"));
array.removeAt(0);
doc.setArray(array);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
QByteArray("[\n"
"]\n"));
}
void tst_QtJson::compactObject()
{
QJsonObject object;
object.insert(QLatin1String("Key1"), QLatin1String("First Entry"));
object.insert(QLatin1String("Key2"), QLatin1String("Second Entry"));
object.insert(QLatin1String("Key3"), QLatin1String("Third Entry"));
QJsonDocument doc(object);
int s = doc.toBinaryData().size();
object.remove(QLatin1String("Key2"));
doc.setObject(object);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
QByteArray("{\n"
" \"Key1\": \"First Entry\",\n"
" \"Key3\": \"Third Entry\"\n"
"}\n"));
object.remove(QLatin1String("Key1"));
doc.setObject(object);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
QByteArray("{\n"
" \"Key3\": \"Third Entry\"\n"
"}\n"));
object.remove(QLatin1String("Key3"));
doc.setObject(object);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
QByteArray("{\n"
"}\n"));
}
void tst_QtJson::validation()
{
// this basically tests that we don't crash on corrupt data
QFile file(testDataDir + "/test.json");
QVERIFY(file.open(QFile::ReadOnly));
QByteArray testJson = file.readAll();
QVERIFY(!testJson.isEmpty());
QJsonDocument doc = QJsonDocument::fromJson(testJson);
QVERIFY(!doc.isNull());
QByteArray binary = doc.toBinaryData();
// only test the first 1000 bytes. Testing the full file takes too long
for (int i = 0; i < 1000; ++i) {
QByteArray corrupted = binary;
corrupted[i] = char(0xff);
QJsonDocument doc = QJsonDocument::fromBinaryData(corrupted);
if (doc.isNull())
continue;
QByteArray json = doc.toJson();
}
QFile file2(testDataDir + "/test3.json");
file2.open(QFile::ReadOnly);
testJson = file2.readAll();
QVERIFY(!testJson.isEmpty());
doc = QJsonDocument::fromJson(testJson);
QVERIFY(!doc.isNull());
binary = doc.toBinaryData();
for (int i = 0; i < binary.size(); ++i) {
QByteArray corrupted = binary;
corrupted[i] = char(0xff);
QJsonDocument doc = QJsonDocument::fromBinaryData(corrupted);
if (doc.isNull())
continue;
QByteArray json = doc.toJson();
corrupted = binary;
corrupted[i] = 0x00;
doc = QJsonDocument::fromBinaryData(corrupted);
if (doc.isNull())
continue;
json = doc.toJson();
}
}
void tst_QtJson::assignToDocument()
{
{
const char *json = "{ \"inner\": { \"key\": true } }";
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonObject o = doc.object();
QJsonValue inner = o.value("inner");
QJsonDocument innerDoc(inner.toObject());
QVERIFY(innerDoc != doc);
QCOMPARE(innerDoc.object(), inner.toObject());
}
{
const char *json = "[ [ true ] ]";
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonArray a = doc.array();
QJsonValue inner = a.at(0);
QJsonDocument innerDoc(inner.toArray());
QVERIFY(innerDoc != doc);
QCOMPARE(innerDoc.array(), inner.toArray());
}
}
void tst_QtJson::testDuplicateKeys()
{
QJsonObject obj;
obj.insert(QLatin1String("foo"), QLatin1String("bar"));
obj.insert(QLatin1String("foo"), QLatin1String("zap"));
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value(QLatin1String("foo")).toString(), QLatin1String("zap"));
}
void tst_QtJson::testCompaction()
{
// modify object enough times to trigger compactionCounter
// and make sure the data is still valid
QJsonObject obj;
for (int i = 0; i < 33; ++i) {
obj.remove(QLatin1String("foo"));
obj.insert(QLatin1String("foo"), QLatin1String("bar"));
}
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value(QLatin1String("foo")).toString(), QLatin1String("bar"));
QJsonDocument doc = QJsonDocument::fromBinaryData(QJsonDocument(obj).toBinaryData());
QVERIFY(!doc.isNull());
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QCOMPARE(doc.object(), obj);
}
void tst_QtJson::testDebugStream()
{
{
// QJsonObject
QJsonObject object;
QTest::ignoreMessage(QtDebugMsg, "QJsonObject()");
qDebug() << object;
object.insert(QLatin1String("foo"), QLatin1String("bar"));
QTest::ignoreMessage(QtDebugMsg, "QJsonObject({\"foo\":\"bar\"})");
qDebug() << object;
}
{
// QJsonArray
QJsonArray array;
QTest::ignoreMessage(QtDebugMsg, "QJsonArray()");
qDebug() << array;
array.append(1);
array.append(QLatin1String("foo"));
QTest::ignoreMessage(QtDebugMsg, "QJsonArray([1,\"foo\"])");
qDebug() << array;
}
{
// QJsonDocument
QJsonDocument doc;
QTest::ignoreMessage(QtDebugMsg, "QJsonDocument()");
qDebug() << doc;
QJsonObject object;
object.insert(QLatin1String("foo"), QLatin1String("bar"));
doc.setObject(object);
QTest::ignoreMessage(QtDebugMsg, "QJsonDocument({\"foo\":\"bar\"})");
qDebug() << doc;
QJsonArray array;
array.append(1);
array.append(QLatin1String("foo"));
QTest::ignoreMessage(QtDebugMsg, "QJsonDocument([1,\"foo\"])");
doc.setArray(array);
qDebug() << doc;
}
{
// QJsonValue
QJsonValue value;
QTest::ignoreMessage(QtDebugMsg, "QJsonValue(null)");
qDebug() << value;
value = QJsonValue(true); // bool
QTest::ignoreMessage(QtDebugMsg, "QJsonValue(bool, true)");
qDebug() << value;
value = QJsonValue((double)4.2); // double
QTest::ignoreMessage(QtDebugMsg, "QJsonValue(double, 4.2)");
qDebug() << value;
value = QJsonValue((int)42); // int
QTest::ignoreMessage(QtDebugMsg, "QJsonValue(double, 42)");
qDebug() << value;
value = QJsonValue(QLatin1String("foo")); // string
QTest::ignoreMessage(QtDebugMsg, "QJsonValue(string, \"foo\")");
qDebug() << value;
QJsonArray array;
array.append(1);
array.append(QLatin1String("foo"));
value = QJsonValue(array); // array
QTest::ignoreMessage(QtDebugMsg, "QJsonValue(array, QJsonArray([1,\"foo\"]))");
qDebug() << value;
QJsonObject object;
object.insert(QLatin1String("foo"), QLatin1String("bar"));
value = QJsonValue(object); // object
QTest::ignoreMessage(QtDebugMsg, "QJsonValue(object, QJsonObject({\"foo\":\"bar\"}))");
qDebug() << value;
}
}
void tst_QtJson::testCompactionError()
{
QJsonObject schemaObject;
schemaObject.insert("_Type", QLatin1String("_SchemaType"));
schemaObject.insert("name", QLatin1String("Address"));
schemaObject.insert("schema", QJsonObject());
{
QJsonObject content(schemaObject);
QJsonDocument doc(content);
QVERIFY(!doc.isNull());
QByteArray hash = QCryptographicHash::hash(doc.toBinaryData(), QCryptographicHash::Md5).toHex();
schemaObject.insert("_Version", QString::fromLatin1(hash.constData(), hash.size()));
}
QJsonObject schema;
schema.insert("streetNumber", schema.value("number").toObject());
schemaObject.insert("schema", schema);
{
QJsonObject content(schemaObject);
content.remove("_Uuid");
content.remove("_Version");
QJsonDocument doc(content);
QVERIFY(!doc.isNull());
QByteArray hash = QCryptographicHash::hash(doc.toBinaryData(), QCryptographicHash::Md5).toHex();
schemaObject.insert("_Version", QString::fromLatin1(hash.constData(), hash.size()));
}
}
void tst_QtJson::parseUnicodeEscapes()
{
const QByteArray json = "[ \"A\\u00e4\\u00C4\" ]";
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonArray array = doc.array();
QString result = QLatin1String("A");
result += QChar(0xe4);
result += QChar(0xc4);
QCOMPARE(array.first().toString(), result);
}
void tst_QtJson::assignObjects()
{
const char *json =
"[ { \"Key\": 1 }, { \"Key\": 2 } ]";
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonArray array = doc.array();
QJsonObject object = array.at(0).toObject();
QCOMPARE(object.value("Key").toDouble(), 1.);
object = array.at(1).toObject();
QCOMPARE(object.value("Key").toDouble(), 2.);
}
void tst_QtJson::assignArrays()
{
const char *json =
"[ [ 1 ], [ 2 ] ]";
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonArray array = doc.array();
QJsonArray inner = array.at(0).toArray() ;
QCOMPARE(inner.at(0).toDouble(), 1.);
inner= array.at(1).toArray();
QCOMPARE(inner.at(0).toDouble(), 2.);
}
void tst_QtJson::testTrailingComma()
{
const char *jsons[] = { "{ \"Key\": 1, }", "[ { \"Key\": 1 }, ]" };
for (unsigned i = 0; i < sizeof(jsons)/sizeof(jsons[0]); ++i) {
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(jsons[i], &error);
QCOMPARE(error.error, QJsonParseError::MissingObject);
}
}
void tst_QtJson::testDetachBug()
{
QJsonObject dynamic;
QJsonObject embedded;
QJsonObject local;
embedded.insert("Key1", QString("Value1"));
embedded.insert("Key2", QString("Value2"));
dynamic.insert(QStringLiteral("Bogus"), QString("bogusValue"));
dynamic.insert("embedded", embedded);
local = dynamic.value("embedded").toObject();
dynamic.remove("embedded");
QCOMPARE(local.keys().size(),2);
local.remove("Key1");
local.remove("Key2");
QCOMPARE(local.keys().size(), 0);
local.insert("Key1", QString("anotherValue"));
QCOMPARE(local.keys().size(), 1);
}
void tst_QtJson::valueEquals()
{
QCOMPARE(QJsonValue(), QJsonValue());
QVERIFY(QJsonValue() != QJsonValue(QJsonValue::Undefined));
QVERIFY(QJsonValue() != QJsonValue(true));
QVERIFY(QJsonValue() != QJsonValue(1.));
QVERIFY(QJsonValue() != QJsonValue(QJsonArray()));
QVERIFY(QJsonValue() != QJsonValue(QJsonObject()));
QCOMPARE(QJsonValue(true), QJsonValue(true));
QVERIFY(QJsonValue(true) != QJsonValue(false));
QVERIFY(QJsonValue(true) != QJsonValue(QJsonValue::Undefined));
QVERIFY(QJsonValue(true) != QJsonValue());
QVERIFY(QJsonValue(true) != QJsonValue(1.));
QVERIFY(QJsonValue(true) != QJsonValue(QJsonArray()));
QVERIFY(QJsonValue(true) != QJsonValue(QJsonObject()));
QCOMPARE(QJsonValue(1), QJsonValue(1));
QVERIFY(QJsonValue(1) != QJsonValue(2));
QCOMPARE(QJsonValue(1), QJsonValue(1.));
QVERIFY(QJsonValue(1) != QJsonValue(1.1));
QVERIFY(QJsonValue(1) != QJsonValue(QJsonValue::Undefined));
QVERIFY(QJsonValue(1) != QJsonValue());
QVERIFY(QJsonValue(1) != QJsonValue(true));
QVERIFY(QJsonValue(1) != QJsonValue(QJsonArray()));
QVERIFY(QJsonValue(1) != QJsonValue(QJsonObject()));
QCOMPARE(QJsonValue(1.), QJsonValue(1.));
QVERIFY(QJsonValue(1.) != QJsonValue(2.));
QVERIFY(QJsonValue(1.) != QJsonValue(QJsonValue::Undefined));
QVERIFY(QJsonValue(1.) != QJsonValue());
QVERIFY(QJsonValue(1.) != QJsonValue(true));
QVERIFY(QJsonValue(1.) != QJsonValue(QJsonArray()));
QVERIFY(QJsonValue(1.) != QJsonValue(QJsonObject()));
QCOMPARE(QJsonValue(QJsonArray()), QJsonValue(QJsonArray()));
QJsonArray nonEmptyArray;
nonEmptyArray.append(true);
QVERIFY(QJsonValue(QJsonArray()) != nonEmptyArray);
QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonValue::Undefined));
QVERIFY(QJsonValue(QJsonArray()) != QJsonValue());
QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(true));
QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(1.));
QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonObject()));
QCOMPARE(QJsonValue(QJsonObject()), QJsonValue(QJsonObject()));
QJsonObject nonEmptyObject;
nonEmptyObject.insert("Key", true);
QVERIFY(QJsonValue(QJsonObject()) != nonEmptyObject);
QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonValue::Undefined));
QVERIFY(QJsonValue(QJsonObject()) != QJsonValue());
QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(true));
QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(1.));
QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonArray()));
QCOMPARE(QJsonValue("foo"), QJsonValue(QLatin1String("foo")));
QCOMPARE(QJsonValue("foo"), QJsonValue(QString("foo")));
QCOMPARE(QJsonValue("\x66\x6f\x6f"), QJsonValue(QString("foo")));
QCOMPARE(QJsonValue("\x62\x61\x72"), QJsonValue("bar"));
QCOMPARE(QJsonValue(UNICODE_NON_CHARACTER), QJsonValue(QString(UNICODE_NON_CHARACTER)));
QCOMPARE(QJsonValue(UNICODE_DJE), QJsonValue(QString(UNICODE_DJE)));
QCOMPARE(QJsonValue("\xc3\xa9"), QJsonValue(QString("\xc3\xa9")));
}
void tst_QtJson::objectEquals_data()
{
QTest::addColumn<QJsonObject>("left");
QTest::addColumn<QJsonObject>("right");
QTest::addColumn<bool>("result");
QTest::newRow("two defaults") << QJsonObject() << QJsonObject() << true;
QJsonObject object1;
object1.insert("property", 1);
QJsonObject object2;
object2["property"] = 1;
QJsonObject 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 << QJsonObject() << false;
QJsonObject empty;
empty.insert("property", 1);
empty.take("property");
QTest::newRow("default vs empty") << QJsonObject() << empty << true;
QTest::newRow("empty vs empty") << empty << empty << true;
QTest::newRow("object vs empty") << object1 << empty << false;
QJsonObject referencedEmpty;
referencedEmpty["undefined"];
QTest::newRow("referenced empty vs referenced empty") << referencedEmpty << referencedEmpty << true;
QTest::newRow("referenced empty vs object") << referencedEmpty << object1 << false;
QJsonObject referencedObject1;
referencedObject1.insert("property", 1);
referencedObject1["undefined"];
QJsonObject referencedObject2;
referencedObject2.insert("property", 1);
referencedObject2["aaaaaaaaa"]; // earlier then "property"
referencedObject2["zzzzzzzzz"]; // after "property"
QTest::newRow("referenced object vs default") << referencedObject1 << QJsonObject() << false;
QTest::newRow("referenced object vs referenced object") << referencedObject1 << referencedObject1 << true;
QTest::newRow("referenced object vs object (different)") << referencedObject1 << object3 << false;
}
void tst_QtJson::objectEquals()
{
QFETCH(QJsonObject, left);
QFETCH(QJsonObject, 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 QJsonValue perspective
QCOMPARE(QJsonValue(left) == QJsonValue(right), result);
QCOMPARE(QJsonValue(left) != QJsonValue(right), !result);
QCOMPARE(QJsonValue(right) == QJsonValue(left), result);
QCOMPARE(QJsonValue(right) != QJsonValue(left), !result);
}
void tst_QtJson::arrayEquals_data()
{
QTest::addColumn<QJsonArray>("left");
QTest::addColumn<QJsonArray>("right");
QTest::addColumn<bool>("result");
QTest::newRow("two defaults") << QJsonArray() << QJsonArray() << true;
QJsonArray array1;
array1.append(1);
QJsonArray array2;
array2.append(2111);
array2[0] = 1;
QJsonArray 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 << QJsonArray() << false;
QJsonArray empty;
empty.append(1);
empty.takeAt(0);
QTest::newRow("default vs empty") << QJsonArray() << empty << true;
QTest::newRow("empty vs default") << empty << QJsonArray() << true;
QTest::newRow("empty vs empty") << empty << empty << true;
QTest::newRow("array vs empty") << array1 << empty << false;
}
void tst_QtJson::arrayEquals()
{
QFETCH(QJsonArray, left);
QFETCH(QJsonArray, 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 QJsonValue perspective
QCOMPARE(QJsonValue(left) == QJsonValue(right), result);
QCOMPARE(QJsonValue(left) != QJsonValue(right), !result);
QCOMPARE(QJsonValue(right) == QJsonValue(left), result);
QCOMPARE(QJsonValue(right) != QJsonValue(left), !result);
}
void tst_QtJson::bom()
{
QFile file(testDataDir + "/bom.json");
file.open(QFile::ReadOnly);
QByteArray json = file.readAll();
// Import json document into a QJsonDocument
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(!doc.isNull());
QCOMPARE(error.error, QJsonParseError::NoError);
}
void tst_QtJson::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 =
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]";
QByteArray json(array_data);
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QVERIFY(!doc.isNull());
QCOMPARE(error.error, QJsonParseError::NoError);
json.prepend('[');
json.append(']');
doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isNull());
QCOMPARE(error.error, QJsonParseError::DeepNesting);
json = QByteArray("true ");
for (int i = 0; i < 1024; ++i) {
json.prepend("{ \"Key\": ");
json.append(" }");
}
doc = QJsonDocument::fromJson(json, &error);
QVERIFY(!doc.isNull());
QCOMPARE(error.error, QJsonParseError::NoError);
json.prepend('[');
json.append(']');
doc = QJsonDocument::fromJson(json, &error);
QVERIFY(doc.isNull());
QCOMPARE(error.error, QJsonParseError::DeepNesting);
}
void tst_QtJson::longStrings()
{
// 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');
QByteArray ba(0x7ff0, 'a');
ba.append(0x8010 - 0x7ff0, 'c');
for (int i = 0x7ff0; i < 0x8010; i++) {
s.append(QLatin1Char('c'));
QMap <QString, QVariant> map;
map["key"] = s;
/* Create a QJsonDocument from the QMap ... */
QJsonDocument d1 = QJsonDocument::fromVariant(QVariant(map));
/* ... and a QByteArray from the QJsonDocument */
QByteArray a1 = d1.toJson();
/* Create a QJsonDocument from the QByteArray ... */
QJsonDocument d2 = QJsonDocument::fromJson(a1);
/* ... and a QByteArray from the QJsonDocument */
QByteArray a2 = d2.toJson();
QCOMPARE(a1, a2);
// Test long keys
QJsonObject o1, o2;
o1[s] = 42;
o2[QLatin1String(ba.data(), i + 1)] = 42;
d1.setObject(o1);
d2.setObject(o2);
a1 = d1.toJson();
a2 = d2.toJson();
QCOMPARE(a1, a2);
}
s = QString(0xfff0, 'a');
ba = QByteArray(0xfff0, 'a');
ba.append(0x10010 - 0xfff0, 'c');
for (int i = 0xfff0; i < 0x10010; i++) {
s.append(QLatin1Char('c'));
QMap <QString, QVariant> map;
map["key"] = s;
/* Create a QJsonDocument from the QMap ... */
QJsonDocument d1 = QJsonDocument::fromVariant(QVariant(map));
/* ... and a QByteArray from the QJsonDocument */
QByteArray a1 = d1.toJson();
/* Create a QJsonDocument from the QByteArray ... */
QJsonDocument d2 = QJsonDocument::fromJson(a1);
/* ... and a QByteArray from the QJsonDocument */
QByteArray a2 = d2.toJson();
QCOMPARE(a1, a2);
// Test long keys
QJsonObject o1, o2;
o1[s] = 42;
o2[QLatin1String(ba.data(), i + 1)] = 42;
d1.setObject(o1);
d2.setObject(o2);
a1 = d1.toJson();
a2 = d2.toJson();
QCOMPARE(a1, a2);
}
}
void tst_QtJson::testJsonValueRefDefault()
{
QJsonObject empty;
QCOMPARE(empty["n/a"].toString(), QString());
QCOMPARE(empty["n/a"].toString("default"), QStringLiteral("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_QtJson::arrayInitializerList()
{
QVERIFY(QJsonArray{}.isEmpty());
QCOMPARE(QJsonArray{"one"}.count(), 1);
QCOMPARE(QJsonArray{1}.count(), 1);
{
QJsonArray a{1.3, "hello", 0};
QCOMPARE(QJsonValue(a[0]), QJsonValue(1.3));
QCOMPARE(QJsonValue(a[1]), QJsonValue("hello"));
QCOMPARE(QJsonValue(a[2]), QJsonValue(0));
QCOMPARE(a.count(), 3);
}
{
QJsonObject o;
o["property"] = 1;
QJsonArray a1 {o};
QCOMPARE(a1.count(), 1);
QCOMPARE(a1[0].toObject(), o);
QJsonArray a2 {o, 23};
QCOMPARE(a2.count(), 2);
QCOMPARE(a2[0].toObject(), o);
QCOMPARE(QJsonValue(a2[1]), QJsonValue(23));
QJsonArray a3 { a1, o, a2 };
QCOMPARE(QJsonValue(a3[0]), QJsonValue(a1));
QCOMPARE(QJsonValue(a3[1]), QJsonValue(o));
QCOMPARE(QJsonValue(a3[2]), QJsonValue(a2));
QJsonArray a4 { 1, QJsonArray{1,2,3}, QJsonArray{"hello", 2}, QJsonObject{{"one", 1}} };
QCOMPARE(a4.count(), 4);
QCOMPARE(QJsonValue(a4[0]), QJsonValue(1));
{
QJsonArray a41 = a4[1].toArray();
QJsonArray a42 = a4[2].toArray();
QJsonObject a43 = a4[3].toObject();
QCOMPARE(a41.count(), 3);
QCOMPARE(a42.count(), 2);
QCOMPARE(a43.count(), 1);
QCOMPARE(QJsonValue(a41[2]), QJsonValue(3));
QCOMPARE(QJsonValue(a42[1]), QJsonValue(2));
QCOMPARE(QJsonValue(a43["one"]), QJsonValue(1));
}
}
}
void tst_QtJson::objectInitializerList()
{
QVERIFY(QJsonObject{}.isEmpty());
{ // one property
QJsonObject one {{"one", 1}};
QCOMPARE(one.count(), 1);
QVERIFY(one.contains("one"));
QCOMPARE(QJsonValue(one["one"]), QJsonValue(1));
}
{ // two properties
QJsonObject two {
{"one", 1},
{"two", 2}
};
QCOMPARE(two.count(), 2);
QVERIFY(two.contains("one"));
QVERIFY(two.contains("two"));
QCOMPARE(QJsonValue(two["one"]), QJsonValue(1));
QCOMPARE(QJsonValue(two["two"]), QJsonValue(2));
}
{ // nested object
QJsonObject object{{"nested", QJsonObject{{"innerProperty", 2}}}};
QCOMPARE(object.count(), 1);
QVERIFY(object.contains("nested"));
QVERIFY(object["nested"].isObject());
QJsonObject nested = object["nested"].toObject();
QCOMPARE(QJsonValue(nested["innerProperty"]), QJsonValue(2));
}
{ // nested array
QJsonObject object{{"nested", QJsonArray{"innerValue", 2.1, "bum cyk cyk"}}};
QCOMPARE(object.count(), 1);
QVERIFY(object.contains("nested"));
QVERIFY(object["nested"].isArray());
QJsonArray nested = object["nested"].toArray();
QCOMPARE(nested.count(), 3);
QCOMPARE(QJsonValue(nested[0]), QJsonValue("innerValue"));
QCOMPARE(QJsonValue(nested[1]), QJsonValue(2.1));
}
}
void tst_QtJson::unicodeKeys()
{
QByteArray json = "{"
"\"x\\u2090_1\": \"hello_1\","
"\"y\\u2090_2\": \"hello_2\","
"\"T\\u2090_3\": \"hello_3\","
"\"xyz_4\": \"hello_4\","
"\"abc_5\": \"hello_5\""
"}";
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
QCOMPARE(error.error, QJsonParseError::NoError);
QJsonObject o = doc.object();
const auto keys = o.keys();
QCOMPARE(keys.size(), 5);
for (const QString &key : keys) {
QString suffix = key.mid(key.indexOf(QLatin1Char('_')));
QCOMPARE(o[key].toString(), QString("hello") + suffix);
}
}
void tst_QtJson::garbageAtEnd()
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson("{},", &error);
QCOMPARE(error.error, QJsonParseError::GarbageAtEnd);
QCOMPARE(error.offset, 2);
QVERIFY(doc.isEmpty());
doc = QJsonDocument::fromJson("{} ", &error);
QCOMPARE(error.error, QJsonParseError::NoError);
QVERIFY(!doc.isEmpty());
}
void tst_QtJson::removeNonLatinKey()
{
const QString nonLatinKeyName = QString::fromUtf8("Атрибут100500");
QJsonObject sourceObject;
sourceObject.insert("code", 1);
sourceObject.remove("code");
sourceObject.insert(nonLatinKeyName, 1);
const QByteArray json = QJsonDocument(sourceObject).toJson();
const QJsonObject restoredObject = QJsonDocument::fromJson(json).object();
QCOMPARE(sourceObject.keys(), restoredObject.keys());
QVERIFY(sourceObject.contains(nonLatinKeyName));
QVERIFY(restoredObject.contains(nonLatinKeyName));
}
void tst_QtJson::documentFromVariant()
{
// Test the valid forms of QJsonDocument::fromVariant.
QString string = QStringLiteral("value");
QStringList strList;
strList.append(string);
QJsonDocument da1 = QJsonDocument::fromVariant(QVariant(strList));
QVERIFY(da1.isArray());
QVariantList list;
list.append(string);
QJsonDocument da2 = QJsonDocument::fromVariant(list);
QVERIFY(da2.isArray());
// As JSON arrays they should be equal.
QCOMPARE(da1.array(), da2.array());
QMap <QString, QVariant> map;
map["key"] = string;
QJsonDocument do1 = QJsonDocument::fromVariant(QVariant(map));
QVERIFY(do1.isObject());
QHash <QString, QVariant> hash;
hash["key"] = string;
QJsonDocument do2 = QJsonDocument::fromVariant(QVariant(hash));
QVERIFY(do2.isObject());
// As JSON objects they should be equal.
QCOMPARE(do1.object(), do2.object());
}
void tst_QtJson::parseErrorOffset_data()
{
QTest::addColumn<QByteArray>("json");
QTest::addColumn<int>("errorOffset");
QTest::newRow("Trailing comma in object") << QByteArray("{ \"value\": false, }") << 19;
QTest::newRow("Trailing comma in object plus whitespace") << QByteArray("{ \"value\": false, } ") << 19;
QTest::newRow("Trailing comma in array") << QByteArray("[ false, ]") << 10;
QTest::newRow("Trailing comma in array plus whitespace") << QByteArray("[ false, ] ") << 10;
QTest::newRow("Missing value in object") << QByteArray("{ \"value\": , } ") << 12;
QTest::newRow("Missing value in array") << QByteArray("[ \"value\" , , ] ") << 13;
QTest::newRow("Leading comma in object") << QByteArray("{ , \"value\": false}") << 3;
QTest::newRow("Leading comma in array") << QByteArray("[ , false]") << 3;
QTest::newRow("Stray ,") << QByteArray(" , ") << 3;
QTest::newRow("Stray [") << QByteArray(" [ ") << 5;
QTest::newRow("Stray }") << QByteArray(" } ") << 3;
}
void tst_QtJson::parseErrorOffset()
{
QFETCH(QByteArray, json);
QFETCH(int, errorOffset);
QJsonParseError error;
QJsonDocument::fromJson(json, &error);
QVERIFY(error.error != QJsonParseError::NoError);
QCOMPARE(error.offset, errorOffset);
}
void tst_QtJson::implicitValueType()
{
QJsonObject rootObject{
{"object", QJsonObject{{"value", 42}}},
{"array", QJsonArray{665, 666, 667}}
};
QJsonValue objectValue = rootObject["object"];
QCOMPARE(objectValue["value"].toInt(), 42);
QCOMPARE(objectValue["missingValue"], QJsonValue(QJsonValue::Undefined));
QCOMPARE(objectValue[123], QJsonValue(QJsonValue::Undefined));
QCOMPARE(objectValue["missingValue"].toInt(123), 123);
QJsonValue arrayValue = rootObject["array"];
QCOMPARE(arrayValue[1].toInt(), 666);
QCOMPARE(arrayValue[-1], QJsonValue(QJsonValue::Undefined));
QCOMPARE(arrayValue["asObject"], QJsonValue(QJsonValue::Undefined));
QCOMPARE(arrayValue[-1].toInt(123), 123);
const QJsonObject constObject = rootObject;
QCOMPARE(constObject["object"]["value"].toInt(), 42);
QCOMPARE(constObject["array"][1].toInt(), 666);
QJsonValue objectAsValue(rootObject);
QCOMPARE(objectAsValue["object"]["value"].toInt(), 42);
QCOMPARE(objectAsValue["array"][1].toInt(), 666);
}
void tst_QtJson::implicitDocumentType()
{
QJsonDocument emptyDocument;
QCOMPARE(emptyDocument["asObject"], QJsonValue(QJsonValue::Undefined));
QCOMPARE(emptyDocument[123], QJsonValue(QJsonValue::Undefined));
QJsonDocument objectDocument(QJsonObject{{"value", 42}});
QCOMPARE(objectDocument["value"].toInt(), 42);
QCOMPARE(objectDocument["missingValue"], QJsonValue(QJsonValue::Undefined));
QCOMPARE(objectDocument[123], QJsonValue(QJsonValue::Undefined));
QCOMPARE(objectDocument["missingValue"].toInt(123), 123);
QJsonDocument arrayDocument(QJsonArray{665, 666, 667});
QCOMPARE(arrayDocument[1].toInt(), 666);
QCOMPARE(arrayDocument[-1], QJsonValue(QJsonValue::Undefined));
QCOMPARE(arrayDocument["asObject"], QJsonValue(QJsonValue::Undefined));
QCOMPARE(arrayDocument[-1].toInt(123), 123);
}
void tst_QtJson::streamSerializationQJsonDocument_data()
{
QTest::addColumn<QJsonDocument>("document");
QTest::newRow("empty") << QJsonDocument();
QTest::newRow("object") << QJsonDocument(QJsonObject{{"value", 42}});
}
void tst_QtJson::streamSerializationQJsonDocument()
{
// Check interface only, implementation is tested through to and from
// json functions.
QByteArray buffer;
QFETCH(QJsonDocument, document);
QJsonDocument output;
QDataStream save(&buffer, QIODevice::WriteOnly);
save << document;
QDataStream load(buffer);
load >> output;
QCOMPARE(output, document);
}
void tst_QtJson::streamSerializationQJsonArray_data()
{
QTest::addColumn<QJsonArray>("array");
QTest::newRow("empty") << QJsonArray();
QTest::newRow("values") << QJsonArray{665, 666, 667};
}
void tst_QtJson::streamSerializationQJsonArray()
{
// Check interface only, implementation is tested through to and from
// json functions.
QByteArray buffer;
QFETCH(QJsonArray, array);
QJsonArray output;
QDataStream save(&buffer, QIODevice::WriteOnly);
save << array;
QDataStream load(buffer);
load >> output;
QCOMPARE(output, array);
}
void tst_QtJson::streamSerializationQJsonObject_data()
{
QTest::addColumn<QJsonObject>("object");
QTest::newRow("empty") << QJsonObject();
QTest::newRow("non-empty") << QJsonObject{{"foo", 665}, {"bar", 666}};
}
void tst_QtJson::streamSerializationQJsonObject()
{
// Check interface only, implementation is tested through to and from
// json functions.
QByteArray buffer;
QFETCH(QJsonObject, object);
QJsonObject output;
QDataStream save(&buffer, QIODevice::WriteOnly);
save << object;
QDataStream load(buffer);
load >> output;
QCOMPARE(output, object);
}
void tst_QtJson::streamSerializationQJsonValue_data()
{
QTest::addColumn<QJsonValue>("value");
QTest::newRow("double") << QJsonValue{665};
QTest::newRow("bool") << QJsonValue{true};
QTest::newRow("string") << QJsonValue{QStringLiteral("bum")};
QTest::newRow("array") << QJsonValue{QJsonArray{12,1,5,6,7}};
QTest::newRow("object") << QJsonValue{QJsonObject{{"foo", 665}, {"bar", 666}}};
// test json escape sequence
QTest::newRow("array with 0xD800") << QJsonValue(QJsonArray{QString(0xD800)});
QTest::newRow("array with 0xDF06,0xD834") << QJsonValue(QJsonArray{QString(0xDF06).append(0xD834)});
}
void tst_QtJson::streamSerializationQJsonValue()
{
QByteArray buffer;
QFETCH(QJsonValue, value);
QJsonValue output;
QDataStream save(&buffer, QIODevice::WriteOnly);
save << value;
QDataStream load(buffer);
load >> output;
QCOMPARE(output, value);
}
void tst_QtJson::streamSerializationQJsonValueEmpty()
{
QByteArray buffer;
{
QJsonValue undef{QJsonValue::Undefined};
QDataStream save(&buffer, QIODevice::WriteOnly);
save << undef;
QDataStream load(buffer);
QJsonValue output;
load >> output;
QVERIFY(output.isUndefined());
}
{
QJsonValue null{QJsonValue::Null};
QDataStream save(&buffer, QIODevice::WriteOnly);
save << null;
QDataStream load(buffer);
QJsonValue output;
load >> output;
QVERIFY(output.isNull());
}
}
void tst_QtJson::streamVariantSerialization()
{
// Check interface only, implementation is tested through to and from
// json functions.
QByteArray buffer;
{
QJsonDocument objectDoc(QJsonArray{665, 666, 667});
QVariant output;
QVariant variant(objectDoc);
QDataStream save(&buffer, QIODevice::WriteOnly);
save << variant;
QDataStream load(buffer);
load >> output;
QCOMPARE(output.userType(), QMetaType::QJsonDocument);
QCOMPARE(output.toJsonDocument(), objectDoc);
}
{
QJsonArray array{665, 666, 667};
QVariant output;
QVariant variant(array);
QDataStream save(&buffer, QIODevice::WriteOnly);
save << variant;
QDataStream load(buffer);
load >> output;
QCOMPARE(output.userType(), QMetaType::QJsonArray);
QCOMPARE(output.toJsonArray(), array);
}
{
QJsonObject obj{{"foo", 42}};
QVariant output;
QVariant variant(obj);
QDataStream save(&buffer, QIODevice::WriteOnly);
save << variant;
QDataStream load(buffer);
load >> output;
QCOMPARE(output.userType(), QMetaType::QJsonObject);
QCOMPARE(output.toJsonObject(), obj);
}
{
QJsonValue value{42};
QVariant output;
QVariant variant(value);
QDataStream save(&buffer, QIODevice::WriteOnly);
save << variant;
QDataStream load(buffer);
load >> output;
QCOMPARE(output.userType(), QMetaType::QJsonValue);
QCOMPARE(output.toJsonValue(), value);
}
}
void tst_QtJson::escapeSurrogateCodePoints_data()
{
QTest::addColumn<QString>("str");
QTest::addColumn<QByteArray>("escStr");
QTest::newRow("0xD800") << QString(0xD800) << QByteArray("\\ud800");
QTest::newRow("0xDF06,0xD834") << QString(0xDF06).append(0xD834) << QByteArray("\\udf06\\ud834");
}
void tst_QtJson::escapeSurrogateCodePoints()
{
QFETCH(QString, str);
QFETCH(QByteArray, escStr);
QJsonArray array;
array.append(str);
QByteArray buffer;
QDataStream save(&buffer, QIODevice::WriteOnly);
save << array;
// verify the buffer has escaped values
QVERIFY(buffer.contains(escStr));
}
QTEST_MAIN(tst_QtJson)
#include "tst_qtjson.moc"