Question

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?

Was it helpful?

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:

  • Make two separate methods. One for handling arrays and one for handling objects. They have different methods and different things you can do, so this makes sense to me. You could even keep the same name so that they are overloaded.
  • Have another method with your common stuff in it. I assume that your function is trying to add some data to either the array or object that is passed. Make a method that creates the data (i.e. QJsonObject createJsonData()) and call it inside both of your methods mentioned above.

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.

OTHER TIPS

You may need to add this:

#include <QJsonArray>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top