Javascript string array to QVariant in C++ API
-
I have a C++ backend API in my QML application which contains a function that expects to receive an argument of differing basic types. It is currently defined as accepting a
QVariant
. (The function itself does not really care what the value type is as it passes it along to an RPC mechanism that converts the variant to a JSON string to make a remote call.)Q_INVOKABLE void f(const QVariant& v) const;
This has worked so far, but I have just encountered a problem in trying to call this function from QML Javascript code in the case where the value is a JS string array:
var value = ["a"]; myApi.f(value);
In the Qt Creator debug environment, the value of the parameter shows as
"<not accessible>"
. The debugger does seem to be aware that it should be a single element array but shows nothing for the[0]
element. Higher up the call-stack, things get opaque but it looks like the argument is actually null (0x0).Am I missing something about how array values are converted between Javascript and C++ in QML?
-
Thanks for the suggestion, but I had already read that page and it seemed to support my expectation that this should just work. I forgot to mention that I am on 5.9.6 in case anything new had been added in this area in more recent releases.
I do have a small update in that, having been inspired by a mention of it in a StackOverflow article that I found, I tried messing about with using QJSValue on the C++ side and that does seem to provide a route for passing the string list value from Javascript to the C++ side. I am exploring doing something like this:
Q_INVOKABLE void f(const QVariant& v) const; Q_INVOKABLE void f(const QJSValue& v) const;
where the second overload delegates to the first, using
v.toVariant()
as argument. -
Are you sure it's not just just an issue with the debugger view?
It should work. What doesqDebug() << v;
outputs? -
Hi @GrecKo
No - I should have said, but I only started digging in the debugger because there was a genuine issue in my application.
I made a small example to test this (listed below). If I pass a simple string value (
"Hello, World!"
) to my api function, the debug output is:QVariant(QString, "Hello, World!")
If I pass an array,
["Hello, World!"]
, the output is:QVariant(QJSValue, )
As mentioned earlier, I can work around this by adding an API function that accepts a
QJSValue
and then performingtoVariant
on it.I also confirmed this behaviour in 5.12.1 (which I have installed but cannot use in production at the moment). I am surprised that I cannot find more discussion of this and still wonder if there is something I am missing.
api.h:
#pragma once #include <QObject> #include <QVariant> class Api : public QObject { Q_OBJECT public: explicit Api(QObject *parent = nullptr); Q_INVOKABLE QString strVal(QVariant v); };
main.cpp:
#include "api.h" #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QtDebug> Api::Api(QObject *parent) : QObject(parent) {} QString Api::strVal(QVariant v) { qDebug() << v; return v.toString(); } int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; Api api; auto context = engine.rootContext(); context->setContextProperty("api", &api); const QUrl url(QStringLiteral("qrc:/main.qml")); engine.load(url); return app.exec(); }
main.qml:
import QtQuick 2.9 import QtQuick.Window 2.2 Window { visible: true; width: 640; height: 480 title: api.strVal("Hello, World!") }
-
I'm giving this one a bump. I won't make a habit of doing this, but I was a little surprised that I didn't get more feedback on this one from some of the QML experts out there as it seems like quite a fundamental thing not to be working. Still keen to understand whether this is a bug in Qt, expected behaviour in Qt or something I am getting wrong.
-
I currently only have very recent versions of Qt at hand (5.14 and later), but there, the array gets treated correctly. Using
if (v.canConvert<QJSValue>()) { auto jsval = v.value<QJSValue>(); if (jsval.isArray()) { QString s = v.value<QJSValue>().property(0).toString(); qDebug() << s; return s; } } return v.toString();
, I'll get the correct value.
-
@FKosmale Thanks - maybe what I was missing was that we expected to see
QJSValue
involved here. I suppose I was expecting that a string list source would automatically have been converted to aQVariantList
withQString
elements and that is what I would have seen passed to the C++.Certainly, if I understand it correctly, your solution is better than mine where I added an overload in the API to accept a
QJSValue
. At least theQJSValue
is hidden in the implementation using your approach.