Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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?



  • @Bob64 hi
    can this page help ?



  • 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.


  • Qt Champions 2018

    Are you sure it's not just just an issue with the debugger view?
    It should work. What does qDebug() << 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 performing toVariant 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 a QVariantList with QString 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 the QJSValue is hidden in the implementation using your approach.


Log in to reply