summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/doc/src/json.qdoc2
-rw-r--r--src/corelib/serialization/qcborvalue_p.h6
-rw-r--r--src/corelib/serialization/qjsondocument.cpp5
-rw-r--r--src/corelib/serialization/qjsonparser.cpp151
-rw-r--r--src/corelib/serialization/qjsonparser_p.h5
-rw-r--r--src/corelib/serialization/qjsonvalue.cpp2
-rw-r--r--tests/auto/corelib/serialization/json/tst_qtjson.cpp315
-rw-r--r--tests/auto/tools/qmakelib/evaltest.cpp2
8 files changed, 393 insertions, 95 deletions
diff --git a/src/corelib/doc/src/json.qdoc b/src/corelib/doc/src/json.qdoc
index 6cae8de0e80..513af68c51c 100644
--- a/src/corelib/doc/src/json.qdoc
+++ b/src/corelib/doc/src/json.qdoc
@@ -19,7 +19,7 @@
modify and save JSON data.
More details about the JSON data format can be found at \l{http://json.org}{json.org}
- and in \l {RFC 4627}.
+ and in \l {RFC 8259}.
\section1 Overview
diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h
index 33eb912a6d9..f6eafce6084 100644
--- a/src/corelib/serialization/qcborvalue_p.h
+++ b/src/corelib/serialization/qcborvalue_p.h
@@ -253,6 +253,12 @@ public:
{
insertAt(elements.size(), v);
}
+ void append(QCborValue &&v)
+ {
+ insertAt(elements.size(), v, MoveContainer);
+ v.container = nullptr;
+ v.t = QCborValue::Undefined;
+ }
QByteArray byteArrayAt(qsizetype idx) const
{
diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp
index fe33ff8e548..d1cefc385f8 100644
--- a/src/corelib/serialization/qjsondocument.cpp
+++ b/src/corelib/serialization/qjsondocument.cpp
@@ -272,6 +272,11 @@ QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *e
if (val.isArray() || val.isMap()) {
result.d = std::make_unique<QJsonDocumentPrivate>();
result.d->value = val;
+ } else if (!val.isUndefined() && error) {
+ // parsed a valid string/number/bool/null,
+ // but QJsonDocument only stores objects and arrays.
+ error->error = QJsonParseError::IllegalValue;
+ error->offset = 0;
}
return result;
}
diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp
index 9ab664cb166..c571777a23b 100644
--- a/src/corelib/serialization/qjsonparser.cpp
+++ b/src/corelib/serialization/qjsonparser.cpp
@@ -61,7 +61,7 @@ using namespace QtMiscUtils;
\value UnterminatedArray The array is not correctly terminated with a closing square bracket
\value MissingValueSeparator A colon separating keys from values inside objects is missing
\value IllegalValue The value is illegal
- \value TerminationByNumber The input stream ended while parsing a number
+ \value TerminationByNumber The input stream ended while parsing a number (as of 6.9, this is no longer returned)
\value IllegalNumber The number is not well formed
\value IllegalEscapeSequence An illegal escape sequence occurred in the input
\value IllegalUTF8String An illegal UTF8 sequence occurred in the input
@@ -161,21 +161,20 @@ class StashedContainer
public:
StashedContainer(QExplicitlySharedDataPointer<QCborContainerPrivate> *container,
QCborValue::Type type)
- : type(type), stashed(std::move(*container)), current(container)
+ : type(type), stashed(std::move(*container))
{
}
- ~StashedContainer()
+ QCborValue intoValue(QExplicitlySharedDataPointer<QCborContainerPrivate> *parent)
{
- stashed->append(QCborContainerPrivate::makeValue(type, -1, current->take(),
- QCborContainerPrivate::MoveContainer));
- *current = std::move(stashed);
+ std::swap(stashed, *parent);
+ return QCborContainerPrivate::makeValue(type, -1, stashed.take(),
+ QCborContainerPrivate::MoveContainer);
}
private:
QCborValue::Type type;
QExplicitlySharedDataPointer<QCborContainerPrivate> stashed;
- QExplicitlySharedDataPointer<QCborContainerPrivate> *current;
};
Parser::Parser(const char *json, int length)
@@ -281,25 +280,27 @@ char Parser::nextToken()
QCborValue Parser::parse(QJsonParseError *error)
{
eatBOM();
- char token = nextToken();
- QCborValue data;
+ char token;
+ QCborValue value;
- if (token == BeginArray) {
- container = new QCborContainerPrivate;
- if (!parseArray())
- goto error;
- data = QCborContainerPrivate::makeValue(QCborValue::Array, -1, container.take(),
- QCborContainerPrivate::MoveContainer);
- } else if (token == BeginObject) {
+ if (!eatSpace()) {
+ lastError = QJsonParseError::IllegalValue;
+ goto error;
+ }
+
+ token = *json;
+ if (token == Quote) {
container = new QCborContainerPrivate;
- if (!parseObject())
+ json++;
+ if (!parseString())
goto error;
- data = QCborContainerPrivate::makeValue(QCborValue::Map, -1, container.take(),
- QCborContainerPrivate::MoveContainer);
+ value = QCborContainerPrivate::makeValue(QCborValue::String, 0, container.take(),
+ QCborContainerPrivate::MoveContainer);
} else {
- lastError = QJsonParseError::IllegalValue;
- goto error;
+ value = parseValue();
+ if (value.isUndefined())
+ goto error;
}
eatSpace();
@@ -314,7 +315,7 @@ QCborValue Parser::parse(QJsonParseError *error)
error->error = QJsonParseError::NoError;
}
- return data;
+ return value;
}
error:
@@ -423,6 +424,20 @@ static void sortContainer(QCborContainerPrivate *container)
container->elements.erase(result.elementsIterator(), container->elements.end());
}
+bool Parser::parseValueIntoContainer()
+{
+ QCborValue value = parseValue();
+ switch (value.type()) {
+ case QCborValue::Undefined:
+ return false; // error while parsing
+ case QCborValue::String:
+ break; // strings were already added
+ default:
+ container->append(std::move(value));
+ }
+
+ return true;
+}
/*
object = begin-object [ member *( value-separator member ) ]
@@ -480,10 +495,8 @@ bool Parser::parseMember()
lastError = QJsonParseError::UnterminatedObject;
return false;
}
- if (!parseValue())
- return false;
- return true;
+ return parseValueIntoContainer();
}
/*
@@ -510,8 +523,10 @@ bool Parser::parseArray()
}
if (!container)
container = new QCborContainerPrivate;
- if (!parseValue())
+
+ if (!parseValueIntoContainer())
return false;
+
char token = nextToken();
if (token == EndArray)
break;
@@ -535,82 +550,81 @@ value = false / null / true / object / array / number / string
*/
-bool Parser::parseValue()
+QCborValue Parser::parseValue()
{
switch (*json++) {
case 'n':
- if (end - json < 4) {
+ if (end - json < 3) {
lastError = QJsonParseError::IllegalValue;
- return false;
+ return QCborValue();
}
if (*json++ == 'u' &&
*json++ == 'l' &&
*json++ == 'l') {
- container->append(QCborValue(QCborValue::Null));
- return true;
+ return QCborValue(QCborValue::Null);
}
lastError = QJsonParseError::IllegalValue;
- return false;
+ return QCborValue();
case 't':
- if (end - json < 4) {
+ if (end - json < 3) {
lastError = QJsonParseError::IllegalValue;
- return false;
+ return QCborValue();
}
if (*json++ == 'r' &&
*json++ == 'u' &&
*json++ == 'e') {
- container->append(QCborValue(true));
- return true;
+ return QCborValue(true);
}
lastError = QJsonParseError::IllegalValue;
- return false;
+ return QCborValue();
case 'f':
- if (end - json < 5) {
+ if (end - json < 4) {
lastError = QJsonParseError::IllegalValue;
- return false;
+ return QCborValue();
}
if (*json++ == 'a' &&
*json++ == 'l' &&
*json++ == 's' &&
*json++ == 'e') {
- container->append(QCborValue(false));
- return true;
+ return QCborValue(false);
}
lastError = QJsonParseError::IllegalValue;
- return false;
+ return QCborValue();
case Quote: {
- if (!parseString())
- return false;
- return true;
+ if (parseString())
+ // strings are already added to the container
+ // callers must check for this type
+ return QCborValue(QCborValue::String);
+
+ return QCborValue();
}
case BeginArray: {
StashedContainer stashedContainer(&container, QCborValue::Array);
- if (!parseArray())
- return false;
- return true;
+ if (parseArray())
+ return stashedContainer.intoValue(&container);
+
+ return QCborValue();
}
case BeginObject: {
StashedContainer stashedContainer(&container, QCborValue::Map);
- if (!parseObject())
- return false;
- return true;
+ if (parseObject())
+ return stashedContainer.intoValue(&container);
+
+ return QCborValue();
}
case ValueSeparator:
// Essentially missing value, but after a colon, not after a comma
// like the other MissingObject errors.
lastError = QJsonParseError::IllegalValue;
- return false;
+ return QCborValue();
case EndObject:
case EndArray:
lastError = QJsonParseError::MissingObject;
- return false;
+ return QCborValue();
default:
--json;
- if (!parseNumber())
- return false;
+ return parseNumber();
}
-
- return true;
}
@@ -631,7 +645,7 @@ bool Parser::parseValue()
*/
-bool Parser::parseNumber()
+QCborValue Parser::parseNumber()
{
const char *start = json;
bool isInt = true;
@@ -667,19 +681,13 @@ bool Parser::parseNumber()
++json;
}
- if (json >= end) {
- lastError = QJsonParseError::TerminationByNumber;
- return false;
- }
-
const QByteArray number = QByteArray::fromRawData(start, json - start);
if (isInt) {
bool ok;
qlonglong n = number.toLongLong(&ok);
if (ok) {
- container->append(QCborValue(n));
- return true;
+ return QCborValue(n);
}
}
@@ -688,16 +696,13 @@ bool Parser::parseNumber()
if (!ok) {
lastError = QJsonParseError::IllegalNumber;
- return false;
+ return QCborValue();
}
qint64 n;
if (convertDoubleTo(d, &n))
- container->append(QCborValue(n));
- else
- container->append(QCborValue(d));
-
- return true;
+ return QCborValue(n);
+ return QCborValue(d);
}
/*
@@ -819,7 +824,7 @@ bool Parser::parseString()
isAscii = false;
}
++json;
- if (json >= end) {
+ if (json > end) {
lastError = QJsonParseError::UnterminatedString;
return false;
}
@@ -855,7 +860,7 @@ bool Parser::parseString()
}
++json;
- if (json >= end) {
+ if (json > end) {
lastError = QJsonParseError::UnterminatedString;
return false;
}
diff --git a/src/corelib/serialization/qjsonparser_p.h b/src/corelib/serialization/qjsonparser_p.h
index 47830a108ee..ef470f5cbcb 100644
--- a/src/corelib/serialization/qjsonparser_p.h
+++ b/src/corelib/serialization/qjsonparser_p.h
@@ -39,8 +39,9 @@ private:
bool parseArray();
bool parseMember();
bool parseString();
- bool parseValue();
- bool parseNumber();
+ bool parseValueIntoContainer();
+ QCborValue parseValue();
+ QCborValue parseNumber();
const char *head;
const char *json;
const char *end;
diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp
index 7ccfb782c33..1a7f4dde467 100644
--- a/src/corelib/serialization/qjsonvalue.cpp
+++ b/src/corelib/serialization/qjsonvalue.cpp
@@ -605,8 +605,6 @@ QVariant QJsonValue::toVariant() const
the optional \a error variable will contain further details about the
error.
- Currently, only objects/maps and arrays/lists can be parsed.
-
\sa QJsonParseError, isUndefined(), toJson()
*/
QJsonValue QJsonValue::fromJson(QByteArrayView json, QJsonParseError *error)
diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
index 571378c199d..0a3bbe4e571 100644
--- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp
+++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
@@ -93,6 +93,10 @@ private Q_SLOTS:
void parseNumbers();
void parseStrings();
void parseDuplicateKeys();
+ void parseTopLevel_data();
+ void parseTopLevel();
+ void parseTopLevelErrors_data();
+ void parseTopLevelErrors();
void testParser();
void assignToDocument();
@@ -2278,161 +2282,313 @@ void tst_QtJson::fromJsonErrors()
{
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "{\n \n\n";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::UnterminatedObject);
QCOMPARE(error.offset, 8);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "{\n \"key\" 10\n";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::MissingNameSeparator);
QCOMPARE(error.offset, 13);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n \n\n";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
QCOMPARE(error.offset, 8);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n 1, true\n\n";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
QCOMPARE(error.offset, 14);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n 1 true\n\n";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::MissingValueSeparator);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n nul";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n nulzz";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 10);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n tru";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n trud]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 10);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n fal";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n falsd]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalValue);
QCOMPARE(error.offset, 11);
}
{
QJsonParseError error;
+ QJsonParseError error2;
+ QByteArray json = "[false";
+ QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
+ QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
+ QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
+ QCOMPARE(error.offset, 6);
+ }
+ {
+ QJsonParseError error;
+ QJsonParseError error2;
+ QByteArray json = "[true";
+ QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
+ QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
+ QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
+ QCOMPARE(error.offset, 5);
+ }
+ {
+ QJsonParseError error;
+ QJsonParseError error2;
+ QByteArray json = "[null";
+ QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
+ QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
+ QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
+ QCOMPARE(error.offset, 5);
+ }
+ {
+ QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n 11111";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
- QCOMPARE(error.error, QJsonParseError::TerminationByNumber);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
+ QCOMPARE(error.error, QJsonParseError::UnterminatedArray);
QCOMPARE(error.offset, 11);
}
{
QJsonParseError error;
+ QJsonParseError error2;
+ QByteArray json = "{\n \"foo\": 0 ";
+ QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
+ QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
+ QCOMPARE(error.error, QJsonParseError::UnterminatedObject);
+ QCOMPARE(error.offset, 15);
+ }
+ {
+ QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n -1E10000]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalNumber);
QCOMPARE(error.offset, 14);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n -1e-10000]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalNumber);
QCOMPARE(error.offset, 15);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n \"\\u12\"]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalEscapeSequence);
QCOMPARE(error.offset, 11);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n \"foo" INVALID_UNICODE "bar\"]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalUTF8String);
QCOMPARE(error.offset, 12);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n \"";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::UnterminatedString);
QCOMPARE(error.offset, 8);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n \"c" UNICODE_DJE "a\\u12\"]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalEscapeSequence);
QCOMPARE(error.offset, 15);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n \"c" UNICODE_DJE "a" INVALID_UNICODE "bar\"]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::IllegalUTF8String);
QCOMPARE(error.offset, 13);
}
{
QJsonParseError error;
+ QJsonParseError error2;
QByteArray json = "[\n \"c" UNICODE_DJE "a ]";
QJsonValue val = QJsonValue::fromJson(json, &error);
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error2);
QVERIFY(val.isUndefined());
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, error2.error);
+ QCOMPARE(error.offset, error2.offset);
QCOMPARE(error.error, QJsonParseError::UnterminatedString);
QCOMPARE(error.offset, 14);
}
@@ -2468,6 +2624,11 @@ void tst_QtJson::parseNumbers()
QJsonValue val = array.at(0);
QCOMPARE(val.type(), QJsonValue::Double);
QCOMPARE(val.toDouble(), (double)numbers[i].n);
+
+ QJsonValue val2 = QJsonValue::fromJson(numbers[i].str);
+ QCOMPARE(val, val2);
+ QCOMPARE(val2.type(), QJsonValue::Double);
+ QCOMPARE(val2.toDouble(), (double)numbers[i].n);
}
}
// test number parsing
@@ -2510,6 +2671,11 @@ void tst_QtJson::parseNumbers()
QJsonValue val = array.at(0);
QCOMPARE(val.type(), QJsonValue::Double);
QCOMPARE(val.toDouble(), numbers[i].n);
+
+ QJsonValue val2 = QJsonValue::fromJson(numbers[i].str);
+ QCOMPARE(val, val2);
+ QCOMPARE(val2.type(), QJsonValue::Double);
+ QCOMPARE(val2.toDouble(), numbers[i].n);
}
}
QT_IGNORE_DEPRECATIONS(constexpr bool has_denorm = std::numeric_limits<double>::has_denorm == std::denorm_present;)
@@ -2532,6 +2698,11 @@ void tst_QtJson::parseNumbers()
QJsonValue val = array.at(0);
QCOMPARE(val.type(), QJsonValue::Double);
QCOMPARE(val.toDouble(), numbers[i].n);
+
+ QJsonValue val2 = QJsonValue::fromJson(numbers[i].str);
+ QCOMPARE(val, val2);
+ QCOMPARE(val2.type(), QJsonValue::Double);
+ QCOMPARE(val2.toDouble(), numbers[i].n);
}
} else {
qInfo("Skipping denormal test as this system's double type lacks support");
@@ -2570,6 +2741,14 @@ void tst_QtJson::parseStrings()
QCOMPARE(val.type(), QJsonValue::String);
QCOMPARE(root.toJson(), json);
+
+ QByteArray jsonStr = "\"";
+ jsonStr += strings[i];
+ jsonStr += '\"';
+ QJsonValue val2 = QJsonValue::fromJson(jsonStr);
+ QCOMPARE(val, val2);
+ QCOMPARE(val2.type(), QJsonValue::String);
+ // TODO: QJsonValue::toJson
}
struct Pairs {
@@ -2601,6 +2780,14 @@ void tst_QtJson::parseStrings()
QCOMPARE(val.type(), QJsonValue::String);
QCOMPARE(root.toJson(), out);
+
+ QByteArray jsonStr = "\"";
+ jsonStr += pairs[i].in;
+ jsonStr += '\"';
+ QJsonValue val2 = QJsonValue::fromJson(jsonStr);
+ QCOMPARE(val, val2);
+ QCOMPARE(val2.type(), QJsonValue::String);
+ // TODO: QJsonValue::toJson
}
}
@@ -2622,6 +2809,77 @@ void tst_QtJson::parseDuplicateKeys()
QCOMPARE(it.value(), QJsonValue(false));
}
+void tst_QtJson::parseTopLevel_data()
+{
+ QTest::addColumn<QByteArrayView>("input");
+ QTest::addColumn<QJsonValue>("result");
+
+ QTest::addRow("true") << QByteArrayView(" true ") << QJsonValue(true);
+ QTest::addRow("false") << QByteArrayView("false") << QJsonValue(false);
+ QTest::addRow("null") << QByteArrayView("null") << QJsonValue(QJsonValue::Null);
+ QTest::addRow("integer") << QByteArrayView(" 42 ") << QJsonValue(42);
+ QTest::addRow("string") << QByteArrayView(" \" a string \" ") << QJsonValue(" a string ");
+ QTest::addRow("garbage object after") << QByteArrayView("true{{{{", 4) << QJsonValue(true);
+ QTest::addRow("garbage 'e' after (true)") << QByteArrayView("truee", 4) << QJsonValue(true);
+ QTest::addRow("garbage 'e' after (false)") << QByteArrayView("falsee", 5) << QJsonValue(false);
+ QTest::addRow("garbage 'l' after (null)")
+ << QByteArrayView("nulll", 4) << QJsonValue(QJsonValue::Null);
+ QTest::addRow("too large integer")
+ << QByteArrayView("18446744073709551616") << QJsonValue(18446744073709551616.0);
+ QTest::addRow("too large integer (lower precision)")
+ << QByteArrayView("18446744073709551616") << QJsonValue(18446744073709552000.0);
+}
+
+void tst_QtJson::parseTopLevel()
+{
+ QFETCH(QByteArrayView, input);
+ QFETCH(QJsonValue, result);
+
+ QJsonParseError error;
+ QJsonValue val = QJsonValue::fromJson(input, &error);
+ QCOMPARE(error.error, QJsonParseError::NoError);
+ QVERIFY(!val.isUndefined());
+ QCOMPARE(val, result);
+ QCOMPARE(val.type(), result.type());
+}
+
+void tst_QtJson::parseTopLevelErrors_data()
+{
+ QTest::addColumn<QByteArrayView>("input");
+ QTest::addColumn<QJsonParseError::ParseError>("parseError");
+ QTest::addColumn<int>("offset");
+
+ QTest::addRow("bad true") << QByteArrayView("truee") << QJsonParseError::GarbageAtEnd << 4;
+ QTest::addRow("bad false") << QByteArrayView("falsee") << QJsonParseError::GarbageAtEnd << 5;
+ QTest::addRow("bad null") << QByteArrayView("nulll") << QJsonParseError::GarbageAtEnd << 4;
+ QTest::addRow("duplicate number")
+ << QByteArrayView(" 42 42 ") << QJsonParseError::GarbageAtEnd << 4;
+ QTest::addRow("unterminated string")
+ << QByteArrayView(" \" a string ") << QJsonParseError::UnterminatedString << 13;
+ QTest::addRow("duplicate quotes")
+ << QByteArrayView(" \" a \"\" ") << QJsonParseError::GarbageAtEnd << 6;
+ QTest::addRow("short true") << QByteArrayView("true", 3) << QJsonParseError::IllegalValue << 1;
+ QTest::addRow("short false") << QByteArrayView("false", 4) << QJsonParseError::IllegalValue
+ << 1;
+ QTest::addRow("short null") << QByteArrayView("null", 3) << QJsonParseError::IllegalValue << 1;
+ QTest::addRow("empty string") << QByteArrayView("") << QJsonParseError::IllegalValue << 0;
+ QTest::addRow("only whitespace")
+ << QByteArrayView(" \t \n \t\t \n \r\r \r\n ") << QJsonParseError::IllegalValue << 17;
+}
+
+void tst_QtJson::parseTopLevelErrors()
+{
+ QFETCH(QByteArrayView, input);
+ QFETCH(QJsonParseError::ParseError, parseError);
+ QFETCH(int, offset);
+
+ QJsonParseError error;
+ QJsonValue val = QJsonValue::fromJson(input, &error);
+ QCOMPARE(error.error, parseError);
+ QCOMPARE(error.offset, offset);
+ QVERIFY(val.isUndefined());
+}
+
void tst_QtJson::testParser()
{
QFile file(testDataDir + "/test.json");
@@ -2779,9 +3037,14 @@ void tst_QtJson::testDebugStream()
}
}
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wformat-security")
+QT_WARNING_DISABLE_CLANG("-Wformat-security")
+
void tst_QtJson::parseEscapes_data()
{
QTest::addColumn<QByteArray>("json");
+ QTest::addColumn<QByteArray>("jsonString");
QTest::addColumn<QString>("result");
auto addUnicodeRow = [](char32_t u) {
@@ -2790,24 +3053,30 @@ void tst_QtJson::parseEscapes_data()
const QString result = QString::fromUcs4(&u, 1);
for (QChar c : result)
ptr += snprintf(ptr, std::end(buf) - ptr, "\\u%04x", c.unicode());
- QTest::addRow("U+%04X", u) << "[\"" + QByteArray(buf) + "\"]" << result;
+ QTest::addRow("U+%04X", u) << "[\"" + QByteArray(buf) + "\"]" << '"' + QByteArray(buf) + '"' << result;
};
char singleCharJson[] = R"(["\x"])";
+ char singleCharJsonStr[] = R"("\x")";
Q_ASSERT(singleCharJson[3] == 'x');
- auto makeSingleCharEscape = [&singleCharJson](char c) {
+ Q_ASSERT(singleCharJsonStr[2] == 'x');
+ auto makeSingleCharEscapeRow = [&](const char *format, char c, const QString &result,
+ auto... formatArgs) {
singleCharJson[3] = char(c);
- return QByteArray(singleCharJson, std::size(singleCharJson) - 1);
+ singleCharJsonStr[2] = char(c);
+ QByteArray json(singleCharJson, std::size(singleCharJson) - 1);
+ QByteArray jsonStr(singleCharJsonStr, std::size(singleCharJsonStr) - 1);
+ QTest::addRow(format, formatArgs...) << json << jsonStr << result;
};
- QTest::addRow("quote") << makeSingleCharEscape('"') << "\"";
- QTest::addRow("backslash") << makeSingleCharEscape('\\') << "\\";
- QTest::addRow("slash") << makeSingleCharEscape('/') << "/";
- QTest::addRow("backspace") << makeSingleCharEscape('b') << "\b";
- QTest::addRow("form-feed") << makeSingleCharEscape('f') << "\f";
- QTest::addRow("newline") << makeSingleCharEscape('n') << "\n";
- QTest::addRow("carriage-return") << makeSingleCharEscape('r') << "\r";
- QTest::addRow("tab") << makeSingleCharEscape('t') << "\t";
+ makeSingleCharEscapeRow("quote", '"', "\"");
+ makeSingleCharEscapeRow("backslash", '\\', "\\");
+ makeSingleCharEscapeRow("slash", '/', "/");
+ makeSingleCharEscapeRow("backspace", 'b', "\b");
+ makeSingleCharEscapeRow("form-feed", 'f', "\f");
+ makeSingleCharEscapeRow("newline", 'n', "\n");
+ makeSingleCharEscapeRow("carriage-return", 'r', "\r");
+ makeSingleCharEscapeRow("tab", 't', "\t");
// we're not going to exhaustively test all Unicode possibilities
for (char16_t c = 0; c < 0x21; ++c)
@@ -2825,7 +3094,8 @@ void tst_QtJson::parseEscapes_data()
addUnicodeRow(U'\U0010ffff');
QTest::addRow("mojibake-utf8")
- << QByteArrayLiteral(R"(["A\u00e4\u00C4"])") << QStringLiteral(u"A\u00e4\u00C4");
+ << QByteArrayLiteral(R"(["A\u00e4\u00C4"])") << QByteArrayLiteral(R"("A\u00e4\u00C4")")
+ << QStringLiteral(u"A\u00e4\u00C4");
// characters for which, preceded by backslash, it is a valid (recognized)
// escape sequence (should match the above list)
@@ -2833,25 +3103,38 @@ void tst_QtJson::parseEscapes_data()
for (int i = 0; i <= 0xff; ++i) {
if (i && strchr(validEscapes, i))
continue;
- QTest::addRow("invalid-uchar-0x%02x", i) << makeSingleCharEscape(i) << QString(char16_t(i));
+
+ makeSingleCharEscapeRow("invalid-uchar-0x%02x", i, QString(char16_t(i)), i);
}
}
+QT_WARNING_POP
+
void tst_QtJson::parseEscapes()
{
QFETCH(QByteArray, json);
+ QFETCH(QByteArray, jsonString);
QFETCH(QString, result);
-
{
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonArray array = doc.array();
-
QCOMPARE(array.first().toString(), result);
}
{
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(jsonString, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, QJsonParseError::IllegalValue);
+ QCOMPARE(error.offset, 0);
+ }
+ {
QJsonValue val = QJsonValue::fromJson(json);
QCOMPARE(val.toArray().first().toString(), result);
}
+ {
+ QJsonValue val = QJsonValue::fromJson(jsonString);
+ QCOMPARE(val.toString(), result);
+ }
}
void tst_QtJson::makeEscapes_data()
diff --git a/tests/auto/tools/qmakelib/evaltest.cpp b/tests/auto/tools/qmakelib/evaltest.cpp
index 9d22aa2758c..bb8cb196f9b 100644
--- a/tests/auto/tools/qmakelib/evaltest.cpp
+++ b/tests/auto/tools/qmakelib/evaltest.cpp
@@ -2331,7 +2331,7 @@ void tst_qmakelib::addTestFunctions(const QString &qindir)
<< "jsontext = not good\n"
"parseJson(jsontext, json): OK = 1"
<< "OK = UNDEF"
- << "##:2: Error parsing JSON at 1:1: illegal value"
+ << "##:2: Error parsing JSON at 1:2: illegal value"
<< true;
QTest::newRow("parseJson(): bad number of arguments")