summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJohannes Grunenberg <nerixdev@outlook.de>2024-11-13 18:08:55 +0100
committerJohannes Grunenberg <nerixdev@outlook.de>2024-11-22 21:25:23 +0100
commit65fda988e92e314d036e760d689b58ce583cef19 (patch)
tree270a424d8581072582ea7f2f8be88a392a804ce2 /src
parent5c3f76840961693276e3e12e3085cd30a300283e (diff)
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>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/doc/src/json.qdoc2
-rw-r--r--src/corelib/serialization/qcborvalue_p.h6
-rw-r--r--src/corelib/serialization/qjsondocument.cpp5
-rw-r--r--src/corelib/serialization/qjsonparser.cpp151
-rw-r--r--src/corelib/serialization/qjsonparser_p.h5
-rw-r--r--src/corelib/serialization/qjsonvalue.cpp2
6 files changed, 93 insertions, 78 deletions
diff --git a/src/corelib/doc/src/json.qdoc b/src/corelib/doc/src/json.qdoc
index 6cae8de0e80..513af68c51c 100644
--- a/src/corelib/doc/src/json.qdoc
+++ b/src/corelib/doc/src/json.qdoc
@@ -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
diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h
index 33eb912a6d9..f6eafce6084 100644
--- a/src/corelib/serialization/qcborvalue_p.h
+++ b/src/corelib/serialization/qcborvalue_p.h
@@ -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
{
diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp
index fe33ff8e548..d1cefc385f8 100644
--- a/src/corelib/serialization/qjsondocument.cpp
+++ b/src/corelib/serialization/qjsondocument.cpp
@@ -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;
}
diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp
index 9ab664cb166..c571777a23b 100644
--- a/src/corelib/serialization/qjsonparser.cpp
+++ b/src/corelib/serialization/qjsonparser.cpp
@@ -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,25 +280,27 @@ 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) {
+ if (!eatSpace()) {
+ lastError = QJsonParseError::IllegalValue;
+ goto error;
+ }
+
+ token = *json;
+ if (token == Quote) {
container = new QCborContainerPrivate;
- if (!parseObject())
+ json++;
+ if (!parseString())
goto error;
- data = QCborContainerPrivate::makeValue(QCborValue::Map, -1, container.take(),
- QCborContainerPrivate::MoveContainer);
+ value = QCborContainerPrivate::makeValue(QCborValue::String, 0, container.take(),
+ QCborContainerPrivate::MoveContainer);
} else {
- lastError = QJsonParseError::IllegalValue;
- goto error;
+ value = parseValue();
+ if (value.isUndefined())
+ goto error;
}
eatSpace();
@@ -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;
}
diff --git a/src/corelib/serialization/qjsonparser_p.h b/src/corelib/serialization/qjsonparser_p.h
index 47830a108ee..ef470f5cbcb 100644
--- a/src/corelib/serialization/qjsonparser_p.h
+++ b/src/corelib/serialization/qjsonparser_p.h
@@ -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;
diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp
index 7ccfb782c33..1a7f4dde467 100644
--- a/src/corelib/serialization/qjsonvalue.cpp
+++ b/src/corelib/serialization/qjsonvalue.cpp
@@ -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)