Added error reporting to QJsonParser
Change-Id: Ib2390c0faf1ed7ada3fc185abce83740ad112929 Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
This commit is contained in:
parent
f63b23afda
commit
908a080006
@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user