c++dbusqdbus

How do I parse Dict of {String, Dict of {String, Variant}} with QDBus?


I am querying NetworkManager's org.freedesktop.NetworkManager.Settings.Connection interface, calling "GetSettings" on it. It returns a Dict of {String, Dict of {String, Variant}} or a{sa{sv}} in Dbus type terms. I'm using QtCreator with Qt4 to build my application.

I cannot seem to get a piece of useable information out of this dictionary. I cannot provide a MVE as it is heavily dependent if NetworkManager and DBus and Qt4 is installed on someone's system.

Here is the method I am developing to get information out of this Dictionary of Strings and Dictionary of Strings and Variants. I can see all the nice data that I want when piping it into qDebug(): qDebug()<<reply.

void GetInfo()
{
    //sysbus just means the system DBus.
    QDBusInterface connSettings("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager/Settings/1", "org.freedesktop.NetworkManager.Settings.Connection", sysbus);
    QDBusMessage reply = connections.call("GetSettings");
    if(reply.type() == QDBusMessage::ReplyMessage)
    {
        //I have tried everything I can imagine here,
        //QVariant g = reply.arguments().at(0).value<QVariant>(); did not work
        //g.canConvert<QMap>(); returns false, in contrast to what KDE says.
        //QDbusArgument g = query.arguments().at(0).value<QDBusArgument>();
        //g.beginMap(); and such don't work
    }
}

It's very hard finding information on parsing a Dict type. The only source I found that provides some info is KDE. It says "The DBus Dict type should map to QMap, example to follow.." and no other hits on Google or examples exist. Maybe I am missing some fundemantal DBus knowledge, but I'm stumped.

I also checked out this excellent answer: How do I extract the returned data from QDBusMessage in a Qt DBus call? but I could not adapt it to parse a dict.

Would anyone know how to get to the last nested QVariant?


Solution

  • Unfortunately, Qts DBUS API is not always the most straightforward to understand, so here's some tips. Basically what I've found that works for me is that you will have to get the DBusArgument from the reply, and that is what is actually used in order to get the actual data. Depending on what you are extracting, you can either override the operator<< and operator>> in your .cpp file(the documentation for QDBusArgument says how to do this), or you can define the type that you want to extract as. The other important thing to note is that QDBusReply::arguments() contains either sending or receiving arguments, depending on if it a reply or a call.

    Anyway, the following works for me(albeit in Qt5, but I expect that it should work in Qt4 as well)

    QDBusInterface connSettings("org.freedesktop.NetworkManager",
                                "/org/freedesktop/NetworkManager/Settings/1",
                                "org.freedesktop.NetworkManager.Settings.Connection",
                                QDBusConnection::systemBus() );
    QDBusMessage reply = connSettings.call("GetSettings");
    
    qDebug() << "Reply below:";
    qDebug() << reply;
    
    qDebug() << "Extracted: ";
    
    // Extract the argument from the reply
    // In this case, we know that we get data back from the method call,
    // but a range check is good here as well(also to ensure that the 
    // reply is a method reply, not an error)
    const QDBusArgument &dbusArg = reply.arguments().at( 0 ).value<QDBusArgument>();
    
    // The important part here: Define the structure type that we want to
    // extract.  Since the DBus type is a{sa{sv}}, that corresponds to a 
    // Map with a key of QString, which maps to another map of
    // QString,QVariant
    QMap<QString,QMap<QString,QVariant> > map;
    dbusArg >> map;
    qDebug() << "Map is: " << map;
    
    // We're just printing out the data in a more user-friendly way here
    for( QString outer_key : map.keys() ){
        QMap<QString,QVariant> innerMap = map.value( outer_key );
    
        qDebug() << "Key: " << outer_key;
        for( QString inner_key : innerMap.keys() ){
            qDebug() << "    " << inner_key << ":" << innerMap.value( inner_key );
        }
    }