mirror of https://github.com/qt/qtbase.git
QJson: Allow parsing any JSON value to QJsonValue
QJsonValue can now parse all its types (arrays, strings, objects, numbers, booleans, and null) at the top-level. QJsonDocument will still return an error when parsing types that aren't objects or arrays. [ChangeLog][QtCore][QJsonValue] QJsonValue now follows RFC 8259 and is thus able to parse any JSON value, not just arrays and objects. QJsonDocument remains at the level of RFC 4627 in that only arrays and objects can be parsed. Fixes: QTBUG-62502 Change-Id: I10f3895a7646953a6f6b5f132196267e700782a1 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
5c3f768409
commit
65fda988e9
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,27 +280,29 @@ 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) {
|
||||
container = new QCborContainerPrivate;
|
||||
if (!parseObject())
|
||||
goto error;
|
||||
data = QCborContainerPrivate::makeValue(QCborValue::Map, -1, container.take(),
|
||||
QCborContainerPrivate::MoveContainer);
|
||||
} else {
|
||||
if (!eatSpace()) {
|
||||
lastError = QJsonParseError::IllegalValue;
|
||||
goto error;
|
||||
}
|
||||
|
||||
token = *json;
|
||||
if (token == Quote) {
|
||||
container = new QCborContainerPrivate;
|
||||
json++;
|
||||
if (!parseString())
|
||||
goto error;
|
||||
value = QCborContainerPrivate::makeValue(QCborValue::String, 0, container.take(),
|
||||
QCborContainerPrivate::MoveContainer);
|
||||
} else {
|
||||
value = parseValue();
|
||||
if (value.isUndefined())
|
||||
goto error;
|
||||
}
|
||||
|
||||
eatSpace();
|
||||
if (json < end) {
|
||||
lastError = QJsonParseError::GarbageAtEnd;
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue