Convert QJsonObject to Javascript object

1.9k Views Asked by At

As qt docs on QJSValue, QJsonObject ins't implicitly convertible to QJSValue, I want to call a javascript function with QJSEngine from C++, the arguments should be passed with QList<QJsValue> to call function of another QJSValue which holds the function itself.

The problem is one of my arguments is QJsonObject, until now i am supposed to convert it to text then calling and passing it to the javascript function that calls to JSON.parse for converting it to object, i am looking for a solution that lets me convert QJsonObject into QJSValue in C++ and call javascript function with object arguments instead of json text.

Currently the code is like something below

QJsonObject obj;
obj["1"] = QString("A");
obj["2"] = QString("B");

QJSValue func = myEngine.evaluate("(function(arg) { var obj = JSON.parse(arg); var res = obj[\"1\"] + obj[\"2\"]; return res; })");
QJSValueList args;
args << QString(QJsonDocument(obj).toJson());
QJSValue res = func.call(args);

I would like to have a function like QJSValue ConvertToQJSValue(QJsonObject object) something like this :

QJsonObject obj;
obj["1"] = QString("A");
obj["2"] = QString("B");

QJSValue func = myEngine.evaluate("(function(arg) { var res = arg[\"1\"] + arg[\"2\"];  return res; })");
QJSValueList args;
args << ConvertToQJSValue(obj);
QJSValue res = func.call(args);
3

There are 3 best solutions below

0
On

As simple as QJSEngine::toScriptValue(const T &). T must be a QJsonObject or QJsonArray, not a QJsonDocument.

QJsonObject obj;
/// ...
QJSonDocument doc(obj);
QJSEngine engine;
QJSValue res;
if (doc.isObject()) 
  res = engine.toScriptValue(doc.object())
else if (doc.isArray())
  res = engine.toScriptValue(doc.array())
else
  res = engine.newErrorObject(
    QJSValue::TypeError, 
    "JSON was neither an object nor array.  Strange, right?"
  );
0
On

Possibly, convert individual QJsonObject values to QJSValue using toScriptValue(), then concatenate them up to the QJSValueList .. and call func to append them up in one QJSValue.

QJSEngine myEngine;
QJsonObject obj;
obj["1"] = QString("A");
obj["2"] = QString("B");
obj["3"] = QString("C");

QJSValue func = myEngine.evaluate("(function() {var args = Array.prototype.slice.call(arguments); var res='';for(i=0; i<args.length; i++) {res += args[i]}; return res})");
QJSValueList args;
for (int i=0 ; i < obj.size(); i++){
    args << myEngine.toScriptValue(obj.value(obj.keys().at(i)));
}
QJSValue res = func.call(args);

I am not sure though if this is the right thing you should do, because you can get the QJSValue directly as an object,

QJSValue res = myEngine.newObject();
for (int i=0; i<args.length(); i++){
    res.setProperty(i,args[i]);
}

or a QJSValue array:

QJSValue objArray = myEngine.newArray(args.length());
for (int x=0; x< args.length();x++){
    objArray.setProperty(x,args[x]);
}
0
On

I had a similar problem. I started going down the path of just walking the tree recursively from the QJsonValue and building a QJSValue from that.

QJSValue Convert(QJSEngine* engine, const QJsonValue& val)
{
    if (val.isBool())
    {
        return QJSValue(val.toBool());
    }
    else if (val.isString())
    {
        return QJSValue(val.toString());
    }
    else if (val.isDouble())
    {
        return QJSValue(val.toDouble());
    }
    else if (val.isNull())
    {
        return QJSValue(QJSValue::NullValue);
    }
    else if (val.isUndefined())
    {
        return QJSValue(QJSValue::UndefinedValue);
    }
    else if (val.isObject())
    {
        QJsonObject obj = val.toObject();
        QJSValue newobj = engine->newObject();
        for (auto itor = obj.begin(); itor != obj.end(); itor++)
        {
            QString key = itor.key();
            QJsonValue value = itor.value();
            QJSValue convertedValue = Convert(engine, value);
            newobj.setProperty(key, convertedValue);
        }
        return newobj;
    }
    else if (val.isArray())
    {
        QJsonArray arr = val.toArray();
        QJSValue newobj = engine->newArray(arr.size());
        for (int i = 0; i < arr.size(); i++)
        {
            QJsonValue value = arr[i];
            QJSValue convertedValue = Convert(engine, value);
            newobj.setProperty(i, convertedValue);
        }
        return newobj;
    }


    // ASSERT(FALSE && "This shouldn't happen");
    return QJSValue(QJSValue::UndefinedValue);

}

In the above code you could pass a QJsonObject or any other type of JSON node for conversion to a QJSValue.

I'm currently going with something closer to your original solution - just invoke JSON.parse from the engine:

QJSValue func = engine->evaluate("JSON.parse");
QJSValueList args;
args.append(jsonText); // jsonText is a QString or QJSValue string
QJSValue result = func.call(args);
return result;