summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArno Rehn <a.rehn@menlosystems.com>2022-10-26 20:05:28 +0200
committerArno Rehn <a.rehn@menlosystems.com>2023-02-09 17:19:50 +0100
commit4b5949bf54aa84eea866da9eb8d0b6834c968282 (patch)
tree77c18eef7e35b692977bf1dfcd6dc93c7e40c006
parent48d2066d6c9b38f29368e5205a5b7f49ab3fa857 (diff)
downloadqtwebchannel-4b5949bf54aa84eea866da9eb8d0b6834c968282.tar.gz
Add server-side support for custom converters
Automatically tries to convert server-side values to/from QJsonValue. Carries on as usual when the conversion fails. Fixes: QTBUG-92902 Change-Id: I89ae7c3bc8490223c9fab41ca513d9277483692e Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp33
-rw-r--r--src/webchannel/qwebchannel.cpp9
-rw-r--r--tests/auto/webchannel/tst_webchannel.cpp37
-rw-r--r--tests/auto/webchannel/tst_webchannel.h15
4 files changed, 76 insertions, 18 deletions
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index 84c3709..c956676 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -782,17 +782,8 @@ QVariant QMetaObjectPublisher::unwrapVariant(const QVariant &value) const
QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType) const
{
QMetaType target(targetType);
- if (targetType == QMetaType::QJsonValue) {
- return QVariant::fromValue(value);
- } else if (targetType == QMetaType::QJsonArray) {
- if (!value.isArray())
- qWarning() << "Cannot not convert non-array argument" << value << "to QJsonArray.";
- return QVariant::fromValue(value.toArray());
- } else if (targetType == QMetaType::QJsonObject) {
- if (!value.isObject())
- qWarning() << "Cannot not convert non-object argument" << value << "to QJsonObject.";
- return QVariant::fromValue(value.toObject());
- } else if (target.flags() & QMetaType::PointerToQObject) {
+
+ if (target.flags() & QMetaType::PointerToQObject) {
QObject *unwrappedObject = unwrapObject(value.toObject()[KEY_ID].toString());
if (unwrappedObject == nullptr)
qWarning() << "Cannot not convert non-object argument" << value << "to QObject*.";
@@ -802,13 +793,18 @@ QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType
return QVariant(target, reinterpret_cast<const void*>(&flagsValue));
}
- // this converts QJsonObjects to QVariantMaps, which is not desired when
- // we want to get a QJsonObject or QJsonValue (see above)
- QVariant variant = unwrapVariant(value.toVariant());
- if (targetType != QMetaType::QVariant && !variant.convert(target)) {
- qWarning() << "Could not convert argument" << value << "to target type" << target.name() << '.';
+ QVariant variant = QJsonValue::fromVariant(value);
+ // Try explicit conversion to the target type first. If that fails, fall
+ // back to generic conversion
+ if (auto converted = variant; converted.convert(target)) {
+ variant = std::move(converted);
+ } else {
+ if (targetType != QMetaType::QVariant) {
+ qWarning() << "Could not convert argument" << value << "to target type" << target.name() << '.';
+ }
+ variant = value.toVariant();
}
- return variant;
+ return unwrapVariant(variant);
}
int QMetaObjectPublisher::conversionScore(const QJsonValue &value, int targetType) const
@@ -981,6 +977,9 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA
if (!map.convert(QMetaType::fromType<QVariantMap>()))
map = result;
return wrapMap(map.value<QVariantMap>(), transport);
+ } else if (auto v = result; v.convert(QMetaType::fromType<QJsonValue>())) {
+ // Support custom converters to QJsonValue
+ return v.value<QJsonValue>();
}
return QJsonValue::fromVariant(result);
diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp
index 60e49e3..54a5c0b 100644
--- a/src/webchannel/qwebchannel.cpp
+++ b/src/webchannel/qwebchannel.cpp
@@ -33,6 +33,12 @@ QT_BEGIN_NAMESPACE
QWebChannel transparently supports QFuture. When a client calls a method that returns a QFuture,
QWebChannel will send a response with the QFuture result only after the QFuture has finished.
+ Custom conversion of types to and from JSON is supported by defining converters with
+ QMetaType::registerConverter() to and from QJsonValue. Note that custom converters from QJsonValue to a concrete
+ type must fail if the QJsonValue does not match the expected format. Otherwise QWebChannel cannot fall back to its
+ default conversion mechanisms.
+ Custom converters are also available on \l{Qt WebChannel JavaScript API}{the JavaScript side}.
+
The C++ QWebChannel API makes it possible to talk to any HTML client, which could run on a local
or even remote machine. The only limitation is that the HTML client supports the JavaScript
features used by \c{qwebchannel.js}. As such, one can interact
@@ -40,7 +46,8 @@ QT_BEGIN_NAMESPACE
There also exists a declarative \l{Qt WebChannel QML Types}{WebChannel API}.
- \sa {Qt WebChannel Standalone Example}, {Qt WebChannel JavaScript API}{JavaScript API}
+ \sa {Qt WebChannel Standalone Example}, {Qt WebChannel JavaScript API}{JavaScript API},
+ QMetaType::registerConverter()
*/
/*!
diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp
index 7bf04dd..7305929 100644
--- a/tests/auto/webchannel/tst_webchannel.cpp
+++ b/tests/auto/webchannel/tst_webchannel.cpp
@@ -21,6 +21,7 @@
#endif
#include <memory>
+#include <optional>
#include <vector>
QT_USE_NAMESPACE
@@ -244,8 +245,34 @@ TestWebChannel::TestWebChannel(QObject *parent)
, m_lastDouble(0)
{
qRegisterMetaType<TestStruct>();
+ Q_ASSERT(QMetaType::fromType<TestStruct>().isEqualityComparable());
+ Q_ASSERT(QMetaType::fromType<TestStruct>().hasRegisteredDebugStreamOperator());
+
qRegisterMetaType<TestStructVector>();
QMetaType::registerConverter<TestStructVector, QVariantList>(convert_to_js);
+
+ QMetaType::registerConverter<TestStruct, QString>();
+
+ QMetaType::registerConverter<TestStruct, QJsonValue>(
+ [](const TestStruct &s) {
+ return QJsonObject {
+ { "__type__", "TestStruct" },
+ { "foo", s.foo },
+ { "bar", s.bar },
+ };
+ });
+
+ QMetaType::registerConverter<QJsonValue, TestStruct>(
+ [](const QJsonValue &value) -> std::optional<TestStruct> {
+ const auto object = value.toObject();
+ if (object.value("__type__").toString() != QStringLiteral("TestStruct"))
+ return std::nullopt;
+
+ return TestStruct {
+ object.value("foo").toInt(),
+ object.value("bar").toInt(),
+ };
+ });
}
TestWebChannel::~TestWebChannel()
@@ -952,6 +979,11 @@ void TestWebChannel::testWrapValues_data()
{"Two", 2}})
<< QJsonValue(QJsonObject{{"One", 1},
{"Two", 2}});
+
+ QTest::addRow("customType") << QVariant::fromValue(TestStruct{42, 7})
+ << QJsonValue(QJsonObject{{"__type__", "TestStruct"},
+ {"foo", 42},
+ {"bar", 7}});
}
void TestWebChannel::testWrapValues()
@@ -993,6 +1025,11 @@ void TestWebChannel::testJsonToVariant_data()
const TestObject::TestFlags flags = TestObject::FirstFlag | TestObject::SecondFlag;
QTest::addRow("flags") << QJsonValue(static_cast<int>(flags))
<< QVariant::fromValue(flags);
+
+ QTest::addRow("customType") << QJsonValue(QJsonObject{{"__type__", "TestStruct"},
+ {"foo", 42},
+ {"bar", 7}})
+ << QVariant::fromValue(TestStruct{42, 7});
}
void TestWebChannel::testJsonToVariant()
diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h
index 9344fdc..290457f 100644
--- a/tests/auto/webchannel/tst_webchannel.h
+++ b/tests/auto/webchannel/tst_webchannel.h
@@ -15,6 +15,7 @@
#if QT_CONFIG(future)
#include <QFuture>
#endif
+#include <QtDebug>
#include <QtWebChannel/QWebChannelAbstractTransport>
@@ -26,7 +27,21 @@ struct TestStruct
{}
int foo;
int bar;
+
+ operator QString() const {
+ return QStringLiteral("TestStruct(foo=%1, bar=%2)").arg(foo).arg(bar);
+ }
};
+inline bool operator==(const TestStruct &a, const TestStruct &b)
+{
+ return a.foo == b.foo && a.bar == b.bar;
+}
+inline QDebug operator<<(QDebug &dbg, const TestStruct &ts)
+{
+ QDebugStateSaver dbgState(dbg);
+ dbg.noquote() << static_cast<QString>(ts);
+ return dbg;
+}
Q_DECLARE_METATYPE(TestStruct)
using TestStructVector = std::vector<TestStruct>;
Q_DECLARE_METATYPE(TestStructVector)