c++qtqjson

Qt - passing reference of QJsonObject or QJsonArray


I'm making Json format data editor with Qt treeview and Qt Json support. I wanna pass QJsonObject or QJsonArray reference parameter to function.

This works:

void makeJsonData(QJsonObject &obj) {
  obj.insert("key", 1234);
}

//call makeJsonData()
QJsonObject jobj;
makeJsonData(jobj);
int keysize = jobj.keys().size(); //1, OK.

But not with this:

//QJsonValue, because it can handle both QJsonObject and QJsonArray
void makeJsonData(QJsonValue &obj) { 
  obj.toObject().insert("key", 1234); //obj is QJsonObject
}

//call makeJsonData()
QJsonObject jobj;
makeJsonData(QJsonValue::fromVariant(jobj)); //fromVariant() to cast QJsonObject to QJsonValue
int keysize = jobj.keys().size(); //0, Fail.

It looks like QJsonValue::toObject() just copies parameter.. How can I use reference of both QJsonObject and QJsonArray with one parameter type?


Solution

  • There are a couple ways I see to solve your problem:

    Option 1 (as mentioned in my comment)

    A dynamic cast can be used like so:

    bool makeJsonData(void* obj) {
        QJsonObject* asObj = dynamic_cast<QJsonObject*>(obj);
        QJsonArray* asArray = dynamic_cast<QJsonArray*>(obj);
    
        if (asObj) {
            //do what you would if it were an object
        }
        else if (asArray) {
            //do what you would if it were an array
        }
        else {
            //cast fail. Returning false to tell the caller that they passed bad data
            //an alternate (probably better) would be to throw an exception
            return false;
        }
    }
    

    Option 2

    I honestly feel that this business with void* is the wrong way to do it. Doing void* stuff is almost always a code smell (it removes compile-time checks that save us from stepping on their own feet) and in this case I think that the way you are doing this needs work. Also, dynamic_cast requires RTTI which may not always be turned on (compiler support, performance issues, etc).

    I took a look at the Qt headers on my machine and as far as I can tell, QJsonObject and QJsonArray don't really inherit from anything, so going down the route of changing the void* to a base type in order to keep a semblance of type checking won't quite work.

    What I would do would be this:

    The idea is to keep code repetition down while still preserving type checking. The time you spend making the one extra method to handle both cases could be far less than the time you will spend debugging code after accidentally passing in something to a void* pointer that you never meant to pass.

    Option 3

    Alternately, you could use QJsonValue, change the return type of the function to QJsonValue, and make it return the new object without modifying the original. Further, the QJsonValue class has those fun isArray/isObject methods that you could use to do something like mentioned earlier. An example:

    QJsonValue makeJsonData(const QJsonValue& val) {
        if (val.isObject()) {
            QJsonObject obj = val.toObject();
            //do your stuff, modifying obj as you please (perhaps calling another method so that this can have less repetition
            return QJsonValue(obj);
        }
        else if (val.isArray()) {
            QJsonArray arr = val.toArray();
            //do your stuff, modifying arr as you please (perhaps calling another method so that this can have less repetition
            return QJsonValue(arr);
        }
        else {
            throw "Invalid Value Type";
        }
    }
    

    I honestly prefer this pattern, but I know there are reasons for going the way you have mentioned such as avoiding gratuitous memory allocations.