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
*/
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);
}
/*!

View File

@ -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;

View File

@ -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,10 +333,15 @@ 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;
}
}
}
DEBUG << "size =" << values.size();
int table = arrayOffset;
@ -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,12 +709,16 @@ 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;
break;
@ -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,12 +756,16 @@ 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);
*(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch);
@ -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;

View File

@ -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) {

View File

@ -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");