summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Christian <andrew.christian@nokia.com>2012-02-17 15:38:23 -0500
committerQt by Nokia <qt-info@nokia.com>2012-02-18 18:08:06 +0100
commit908a080006faff333b061b69b0dc0fd9cab36114 (patch)
tree3b9a1ee39a3d44643180a74febb7dd441d56326f
parentf63b23afdac47bbf71a7b72fdd965c55b2f60602 (diff)
Added error reporting to QJsonParser
Change-Id: Ib2390c0faf1ed7ada3fc185abce83740ad112929 Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
-rw-r--r--src/corelib/json/qjsondocument.cpp4
-rw-r--r--src/corelib/json/qjsondocument.h22
-rw-r--r--src/corelib/json/qjsonparser.cpp80
-rw-r--r--src/corelib/json/qjsonparser_p.h4
-rw-r--r--tests/auto/corelib/json/tst_qtjson.cpp165
5 files changed, 252 insertions, 23 deletions
diff --git a/src/corelib/json/qjsondocument.cpp b/src/corelib/json/qjsondocument.cpp
index 5ae1bcbe4e..8a7fa760ae 100644
--- a/src/corelib/json/qjsondocument.cpp
+++ b/src/corelib/json/qjsondocument.cpp
@@ -317,10 +317,10 @@ QByteArray QJsonDocument::toJson() const
\sa toJson
*/
-QJsonDocument QJsonDocument::fromJson(const QByteArray &json)
+QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error)
{
QJsonPrivate::Parser parser(json.constData(), json.length());
- return parser.parse();
+ return parser.parse(error);
}
/*!
diff --git a/src/corelib/json/qjsondocument.h b/src/corelib/json/qjsondocument.h
index 39db830726..7eca0302db 100644
--- a/src/corelib/json/qjsondocument.h
+++ b/src/corelib/json/qjsondocument.h
@@ -54,6 +54,26 @@ namespace QJsonPrivate {
class Parser;
}
+struct Q_CORE_EXPORT QJsonParseError
+{
+ enum ParseError {
+ NoError = 0,
+ UnterminatedObject,
+ MissingNameSeparator,
+ UnterminatedArray,
+ MissingValueSeparator,
+ IllegalValue,
+ EndOfNumber,
+ IllegalNumber,
+ StringEscapeSequence,
+ StringUTF8Scan,
+ EndOfString
+ };
+
+ int offset;
+ ParseError error;
+};
+
class Q_CORE_EXPORT QJsonDocument
{
public:
@@ -85,7 +105,7 @@ public:
static QJsonDocument fromVariant(const QVariant &variant);
QVariant toVariant() const;
- static QJsonDocument fromJson(const QByteArray &json);
+ static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = 0);
QByteArray toJson() const;
bool isEmpty() const;
diff --git a/src/corelib/json/qjsonparser.cpp b/src/corelib/json/qjsonparser.cpp
index 75ed7de48f..16eedadf1a 100644
--- a/src/corelib/json/qjsonparser.cpp
+++ b/src/corelib/json/qjsonparser.cpp
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
using namespace QJsonPrivate;
Parser::Parser(const char *json, int length)
- : json(json), data(0), dataLength(0), current(0)
+ : head(json), json(json), data(0), dataLength(0), current(0), lastError(QJsonParseError::NoError)
{
end = json + length;
}
@@ -134,8 +134,6 @@ char Parser::nextToken()
case BeginObject:
case NameSeparator:
case ValueSeparator:
- if (!eatSpace())
- return 0;
case EndArray:
case EndObject:
eatSpace();
@@ -151,7 +149,7 @@ char Parser::nextToken()
/*
JSON-text = object / array
*/
-QJsonDocument Parser::parse()
+QJsonDocument Parser::parse(QJsonParseError *error)
{
#ifdef PARSER_DEBUG
indent = 0;
@@ -182,6 +180,10 @@ QJsonDocument Parser::parse()
END;
{
+ if (error) {
+ error->offset = 0;
+ error->error = QJsonParseError::NoError;
+ }
QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current);
return QJsonDocument(d);
}
@@ -190,6 +192,10 @@ error:
#ifdef PARSER_DEBUG
qDebug() << ">>>>> parser error";
#endif
+ if (error) {
+ error->offset = json - head;
+ error->error = lastError;
+ }
free(data);
return QJsonDocument();
}
@@ -241,8 +247,10 @@ bool Parser::parseObject()
}
DEBUG << "end token=" << token;
- if (token != EndObject)
+ if (token != EndObject) {
+ lastError = QJsonParseError::UnterminatedObject;
return false;
+ }
DEBUG << "numEntries" << parsedObject.offsets.size();
int table = objectOffset;
@@ -283,8 +291,10 @@ bool Parser::parseMember(int baseOffset)
if (!parseString(&latin1))
return false;
char token = nextToken();
- if (token != NameSeparator)
+ if (token != NameSeparator) {
+ lastError = QJsonParseError::MissingNameSeparator;
return false;
+ }
QJsonPrivate::Value val;
if (!parseValue(&val, baseOffset))
return false;
@@ -308,8 +318,10 @@ bool Parser::parseArray()
QVarLengthArray<QJsonPrivate::Value> values;
- if (!eatSpace())
+ if (!eatSpace()) {
+ lastError = QJsonParseError::UnterminatedArray;
return false;
+ }
if (*json == EndArray) {
nextToken();
} else {
@@ -321,8 +333,13 @@ bool Parser::parseArray()
char token = nextToken();
if (token == EndArray)
break;
- else if (token != ValueSeparator)
+ else if (token != ValueSeparator) {
+ if (!eatSpace())
+ lastError = QJsonParseError::UnterminatedArray;
+ else
+ lastError = QJsonParseError::MissingValueSeparator;
return false;
+ }
}
}
@@ -358,8 +375,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
switch (*json++) {
case 'n':
- if (end - json < 4)
+ if (end - json < 4) {
+ lastError = QJsonParseError::IllegalValue;
return false;
+ }
if (*json++ == 'u' &&
*json++ == 'l' &&
*json++ == 'l') {
@@ -368,10 +387,13 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
END;
return true;
}
+ lastError = QJsonParseError::IllegalValue;
return false;
case 't':
- if (end - json < 4)
+ if (end - json < 4) {
+ lastError = QJsonParseError::IllegalValue;
return false;
+ }
if (*json++ == 'r' &&
*json++ == 'u' &&
*json++ == 'e') {
@@ -381,10 +403,13 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
END;
return true;
}
+ lastError = QJsonParseError::IllegalValue;
return false;
case 'f':
- if (end - json < 5)
+ if (end - json < 5) {
+ lastError = QJsonParseError::IllegalValue;
return false;
+ }
if (*json++ == 'a' &&
*json++ == 'l' &&
*json++ == 's' &&
@@ -395,6 +420,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
END;
return true;
}
+ lastError = QJsonParseError::IllegalValue;
return false;
case Quote: {
val->type = QJsonValue::String;
@@ -490,8 +516,10 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
++json;
}
- if (json >= end)
+ if (json >= end) {
+ lastError = QJsonParseError::EndOfNumber;
return false;
+ }
QByteArray number(start, json - start);
DEBUG << "numberstring" << number;
@@ -514,8 +542,10 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
};
d = number.toDouble(&ok);
- if (!ok)
+ if (!ok) {
+ lastError = QJsonParseError::IllegalNumber;
return false;
+ }
int pos = reserveSpace(sizeof(double));
*(quint64 *)(data + pos) = qToLittleEndian(ui);
@@ -679,11 +709,15 @@ bool Parser::parseString(bool *latin1)
if (*json == '"')
break;
else if (*json == '\\') {
- if (!scanEscapeSequence(json, end, &ch))
+ if (!scanEscapeSequence(json, end, &ch)) {
+ lastError = QJsonParseError::StringEscapeSequence;
return false;
+ }
} else {
- if (!scanUtf8Char(json, end, &ch))
+ if (!scanUtf8Char(json, end, &ch)) {
+ lastError = QJsonParseError::StringUTF8Scan;
return false;
+ }
}
if (ch > 0xff) {
*latin1 = false;
@@ -695,8 +729,10 @@ bool Parser::parseString(bool *latin1)
}
++json;
DEBUG << "end of string";
- if (json >= end)
+ if (json >= end) {
+ lastError = QJsonParseError::EndOfString;
return false;
+ }
// no unicode string, we are done
if (*latin1) {
@@ -720,11 +756,15 @@ bool Parser::parseString(bool *latin1)
if (*json == '"')
break;
else if (*json == '\\') {
- if (!scanEscapeSequence(json, end, &ch))
+ if (!scanEscapeSequence(json, end, &ch)) {
+ lastError = QJsonParseError::StringEscapeSequence;
return false;
+ }
} else {
- if (!scanUtf8Char(json, end, &ch))
+ if (!scanUtf8Char(json, end, &ch)) {
+ lastError = QJsonParseError::StringUTF8Scan;
return false;
+ }
}
if (ch > 0xffff) {
int pos = reserveSpace(4);
@@ -737,8 +777,10 @@ bool Parser::parseString(bool *latin1)
}
++json;
- if (json >= end)
+ if (json >= end) {
+ lastError = QJsonParseError::EndOfString;
return false;
+ }
// write string length
*(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2;
diff --git a/src/corelib/json/qjsonparser_p.h b/src/corelib/json/qjsonparser_p.h
index eae6c08718..20e57dcf26 100644
--- a/src/corelib/json/qjsonparser_p.h
+++ b/src/corelib/json/qjsonparser_p.h
@@ -65,7 +65,7 @@ class Parser
public:
Parser(const char *json, int length);
- QJsonDocument parse();
+ QJsonDocument parse(QJsonParseError *error);
class ParsedObject
{
@@ -93,12 +93,14 @@ private:
bool parseString(bool *latin1);
bool parseValue(QJsonPrivate::Value *val, int baseOffset);
bool parseNumber(QJsonPrivate::Value *val, int baseOffset);
+ const char *head;
const char *json;
const char *end;
char *data;
int dataLength;
int current;
+ QJsonParseError::ParseError lastError;
inline int reserveSpace(int space) {
if (current + space >= dataLength) {
diff --git a/tests/auto/corelib/json/tst_qtjson.cpp b/tests/auto/corelib/json/tst_qtjson.cpp
index 323546b8ea..2ac0574ec5 100644
--- a/tests/auto/corelib/json/tst_qtjson.cpp
+++ b/tests/auto/corelib/json/tst_qtjson.cpp
@@ -92,6 +92,7 @@ private Q_SLOTS:
void toJson();
void fromJson();
+ void fromJsonErrors();
void fromBinary();
void toAndFromBinary_data();
void toAndFromBinary();
@@ -1080,6 +1081,170 @@ void TestQtJson::fromJson()
}
}
+void TestQtJson::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::EndOfNumber);
+ 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::StringEscapeSequence);
+ QCOMPARE(error.offset, 11);
+ }
+ {
+ QJsonParseError error;
+ QByteArray json = "[\n \"foo\uffffbar\"]";
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, QJsonParseError::StringUTF8Scan);
+ QCOMPARE(error.offset, 13);
+ }
+ {
+ QJsonParseError error;
+ QByteArray json = "[\n \"";
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, QJsonParseError::EndOfString);
+ QCOMPARE(error.offset, 8);
+ }
+ {
+ QJsonParseError error;
+ QByteArray json = "[\n \"cЂa\\u12\"]";
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, QJsonParseError::StringEscapeSequence);
+ QCOMPARE(error.offset, 15);
+ }
+ {
+ QJsonParseError error;
+ QByteArray json = "[\n \"cЂa\uffffbar\"]";
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, QJsonParseError::StringUTF8Scan);
+ QCOMPARE(error.offset, 14);
+ }
+ {
+ QJsonParseError error;
+ QByteArray json = "[\n \"cЂa ]";
+ QJsonDocument doc = QJsonDocument::fromJson(json, &error);
+ QVERIFY(doc.isEmpty());
+ QCOMPARE(error.error, QJsonParseError::EndOfString);
+ QCOMPARE(error.offset, 14);
+ }
+}
+
void TestQtJson::fromBinary()
{
QFile file(testDataDir + "/test.json");