Added error reporting to QJsonParser

Change-Id: Ib2390c0faf1ed7ada3fc185abce83740ad112929
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
This commit is contained in:
Andrew Christian 2012-02-17 15:38:23 -05:00 committed by Qt by Nokia
parent f63b23afda
commit 908a080006
5 changed files with 252 additions and 23 deletions

View File

@ -317,10 +317,10 @@ QByteArray QJsonDocument::toJson() const
\sa toJson \sa toJson
*/ */
QJsonDocument QJsonDocument::fromJson(const QByteArray &json) QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error)
{ {
QJsonPrivate::Parser parser(json.constData(), json.length()); QJsonPrivate::Parser parser(json.constData(), json.length());
return parser.parse(); return parser.parse(error);
} }
/*! /*!

View File

@ -54,6 +54,26 @@ namespace QJsonPrivate {
class Parser; 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 class Q_CORE_EXPORT QJsonDocument
{ {
public: public:
@ -85,7 +105,7 @@ public:
static QJsonDocument fromVariant(const QVariant &variant); static QJsonDocument fromVariant(const QVariant &variant);
QVariant toVariant() const; QVariant toVariant() const;
static QJsonDocument fromJson(const QByteArray &json); static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = 0);
QByteArray toJson() const; QByteArray toJson() const;
bool isEmpty() const; bool isEmpty() const;

View File

@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
using namespace QJsonPrivate; using namespace QJsonPrivate;
Parser::Parser(const char *json, int length) 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; end = json + length;
} }
@ -134,8 +134,6 @@ char Parser::nextToken()
case BeginObject: case BeginObject:
case NameSeparator: case NameSeparator:
case ValueSeparator: case ValueSeparator:
if (!eatSpace())
return 0;
case EndArray: case EndArray:
case EndObject: case EndObject:
eatSpace(); eatSpace();
@ -151,7 +149,7 @@ char Parser::nextToken()
/* /*
JSON-text = object / array JSON-text = object / array
*/ */
QJsonDocument Parser::parse() QJsonDocument Parser::parse(QJsonParseError *error)
{ {
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
indent = 0; indent = 0;
@ -182,6 +180,10 @@ QJsonDocument Parser::parse()
END; END;
{ {
if (error) {
error->offset = 0;
error->error = QJsonParseError::NoError;
}
QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current); QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current);
return QJsonDocument(d); return QJsonDocument(d);
} }
@ -190,6 +192,10 @@ error:
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
qDebug() << ">>>>> parser error"; qDebug() << ">>>>> parser error";
#endif #endif
if (error) {
error->offset = json - head;
error->error = lastError;
}
free(data); free(data);
return QJsonDocument(); return QJsonDocument();
} }
@ -241,8 +247,10 @@ bool Parser::parseObject()
} }
DEBUG << "end token=" << token; DEBUG << "end token=" << token;
if (token != EndObject) if (token != EndObject) {
lastError = QJsonParseError::UnterminatedObject;
return false; return false;
}
DEBUG << "numEntries" << parsedObject.offsets.size(); DEBUG << "numEntries" << parsedObject.offsets.size();
int table = objectOffset; int table = objectOffset;
@ -283,8 +291,10 @@ bool Parser::parseMember(int baseOffset)
if (!parseString(&latin1)) if (!parseString(&latin1))
return false; return false;
char token = nextToken(); char token = nextToken();
if (token != NameSeparator) if (token != NameSeparator) {
lastError = QJsonParseError::MissingNameSeparator;
return false; return false;
}
QJsonPrivate::Value val; QJsonPrivate::Value val;
if (!parseValue(&val, baseOffset)) if (!parseValue(&val, baseOffset))
return false; return false;
@ -308,8 +318,10 @@ bool Parser::parseArray()
QVarLengthArray<QJsonPrivate::Value> values; QVarLengthArray<QJsonPrivate::Value> values;
if (!eatSpace()) if (!eatSpace()) {
lastError = QJsonParseError::UnterminatedArray;
return false; return false;
}
if (*json == EndArray) { if (*json == EndArray) {
nextToken(); nextToken();
} else { } else {
@ -321,10 +333,15 @@ bool Parser::parseArray()
char token = nextToken(); char token = nextToken();
if (token == EndArray) if (token == EndArray)
break; break;
else if (token != ValueSeparator) else if (token != ValueSeparator) {
if (!eatSpace())
lastError = QJsonParseError::UnterminatedArray;
else
lastError = QJsonParseError::MissingValueSeparator;
return false; return false;
} }
} }
}
DEBUG << "size =" << values.size(); DEBUG << "size =" << values.size();
int table = arrayOffset; int table = arrayOffset;
@ -358,8 +375,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
switch (*json++) { switch (*json++) {
case 'n': case 'n':
if (end - json < 4) if (end - json < 4) {
lastError = QJsonParseError::IllegalValue;
return false; return false;
}
if (*json++ == 'u' && if (*json++ == 'u' &&
*json++ == 'l' && *json++ == 'l' &&
*json++ == 'l') { *json++ == 'l') {
@ -368,10 +387,13 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
END; END;
return true; return true;
} }
lastError = QJsonParseError::IllegalValue;
return false; return false;
case 't': case 't':
if (end - json < 4) if (end - json < 4) {
lastError = QJsonParseError::IllegalValue;
return false; return false;
}
if (*json++ == 'r' && if (*json++ == 'r' &&
*json++ == 'u' && *json++ == 'u' &&
*json++ == 'e') { *json++ == 'e') {
@ -381,10 +403,13 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
END; END;
return true; return true;
} }
lastError = QJsonParseError::IllegalValue;
return false; return false;
case 'f': case 'f':
if (end - json < 5) if (end - json < 5) {
lastError = QJsonParseError::IllegalValue;
return false; return false;
}
if (*json++ == 'a' && if (*json++ == 'a' &&
*json++ == 'l' && *json++ == 'l' &&
*json++ == 's' && *json++ == 's' &&
@ -395,6 +420,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
END; END;
return true; return true;
} }
lastError = QJsonParseError::IllegalValue;
return false; return false;
case Quote: { case Quote: {
val->type = QJsonValue::String; val->type = QJsonValue::String;
@ -490,8 +516,10 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
++json; ++json;
} }
if (json >= end) if (json >= end) {
lastError = QJsonParseError::EndOfNumber;
return false; return false;
}
QByteArray number(start, json - start); QByteArray number(start, json - start);
DEBUG << "numberstring" << number; DEBUG << "numberstring" << number;
@ -514,8 +542,10 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
}; };
d = number.toDouble(&ok); d = number.toDouble(&ok);
if (!ok) if (!ok) {
lastError = QJsonParseError::IllegalNumber;
return false; return false;
}
int pos = reserveSpace(sizeof(double)); int pos = reserveSpace(sizeof(double));
*(quint64 *)(data + pos) = qToLittleEndian(ui); *(quint64 *)(data + pos) = qToLittleEndian(ui);
@ -679,12 +709,16 @@ bool Parser::parseString(bool *latin1)
if (*json == '"') if (*json == '"')
break; break;
else if (*json == '\\') { else if (*json == '\\') {
if (!scanEscapeSequence(json, end, &ch)) if (!scanEscapeSequence(json, end, &ch)) {
lastError = QJsonParseError::StringEscapeSequence;
return false; return false;
}
} else { } else {
if (!scanUtf8Char(json, end, &ch)) if (!scanUtf8Char(json, end, &ch)) {
lastError = QJsonParseError::StringUTF8Scan;
return false; return false;
} }
}
if (ch > 0xff) { if (ch > 0xff) {
*latin1 = false; *latin1 = false;
break; break;
@ -695,8 +729,10 @@ bool Parser::parseString(bool *latin1)
} }
++json; ++json;
DEBUG << "end of string"; DEBUG << "end of string";
if (json >= end) if (json >= end) {
lastError = QJsonParseError::EndOfString;
return false; return false;
}
// no unicode string, we are done // no unicode string, we are done
if (*latin1) { if (*latin1) {
@ -720,12 +756,16 @@ bool Parser::parseString(bool *latin1)
if (*json == '"') if (*json == '"')
break; break;
else if (*json == '\\') { else if (*json == '\\') {
if (!scanEscapeSequence(json, end, &ch)) if (!scanEscapeSequence(json, end, &ch)) {
lastError = QJsonParseError::StringEscapeSequence;
return false; return false;
}
} else { } else {
if (!scanUtf8Char(json, end, &ch)) if (!scanUtf8Char(json, end, &ch)) {
lastError = QJsonParseError::StringUTF8Scan;
return false; return false;
} }
}
if (ch > 0xffff) { if (ch > 0xffff) {
int pos = reserveSpace(4); int pos = reserveSpace(4);
*(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch); *(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch);
@ -737,8 +777,10 @@ bool Parser::parseString(bool *latin1)
} }
++json; ++json;
if (json >= end) if (json >= end) {
lastError = QJsonParseError::EndOfString;
return false; return false;
}
// write string length // write string length
*(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2; *(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2;

View File

@ -65,7 +65,7 @@ class Parser
public: public:
Parser(const char *json, int length); Parser(const char *json, int length);
QJsonDocument parse(); QJsonDocument parse(QJsonParseError *error);
class ParsedObject class ParsedObject
{ {
@ -93,12 +93,14 @@ private:
bool parseString(bool *latin1); bool parseString(bool *latin1);
bool parseValue(QJsonPrivate::Value *val, int baseOffset); bool parseValue(QJsonPrivate::Value *val, int baseOffset);
bool parseNumber(QJsonPrivate::Value *val, int baseOffset); bool parseNumber(QJsonPrivate::Value *val, int baseOffset);
const char *head;
const char *json; const char *json;
const char *end; const char *end;
char *data; char *data;
int dataLength; int dataLength;
int current; int current;
QJsonParseError::ParseError lastError;
inline int reserveSpace(int space) { inline int reserveSpace(int space) {
if (current + space >= dataLength) { if (current + space >= dataLength) {

View File

@ -92,6 +92,7 @@ private Q_SLOTS:
void toJson(); void toJson();
void fromJson(); void fromJson();
void fromJsonErrors();
void fromBinary(); void fromBinary();
void toAndFromBinary_data(); void toAndFromBinary_data();
void toAndFromBinary(); 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() void TestQtJson::fromBinary()
{ {
QFile file(testDataDir + "/test.json"); QFile file(testDataDir + "/test.json");