Modify nested QJsonValue



  • Hello,

    please consider a JSON-structure like the following one:

    @{
    "TestObject": {
    "Name": "foo",
    "TestObject2": {
    "FooAttribute" : "bar"
    }
    }
    }@

    Now with Qt 5, using the JSON-classes, I read this structure using some code like

    @QJsonDocument tmpDoc = QJsonDocument::fromJson(text_from_file);
    QJsonObject tmpObject = tmpDoc.object();@

    Now I want to store the tmpObject and work with it - and reading it is no problem. But what if I actually want to manipulate the "FooAttribute" of "TestObject2"?

    I tried something like this:
    @tmpObject["TestObject"].toObject()["TestObject2"].toObject()["FooAttribute"] = QJsonValue(QString("test"))@

    but since QJsonValue and QJsonObject are implicitly shared and creating a deep copy when being modified, that's exactly what happens here, so the original tmpObject doesn't get manipulated.

    Using the []-Operator I can get write access to the QJsonObject, but as soon as I convert a QJsonValue I create an (implicitly shared) copy.

    Do I really have to create a deep copy and then reassign it to tmpObject, or is there a simple and (most importantly) efficient way to manipulate "FooAttribute" within tmpObjet?

    Thank you very much!
    D.



  • Apparently the Qt Dev Team is working on this according to "this":https://bugreports.qt-project.org/browse/QTBUG-29095



  • I don't think this is the same issue ... I would say it is worth to crate an additional issue entry for that
    It's definitely not intuitive ... e.g. the Qt-Xml-Dom-API behaves in a different way ...

    I think putting it in one code-block makes it more clear:
    @
    QByteArray jsonStr =
    " {"
    " "TestObject": {"
    " "Name": "foo","
    " "TestObject2": {"
    " "FooAttribute" : "bar""
    " }"
    " }"
    " }";

    QJsonDocument tmpDoc = QJsonDocument::fromJson(jsonStr);
    QJsonObject tmpObject = tmpDoc.object();

    tmpObject["TestObject"].toObject()["TestObject2"].toObject()["FooAttribute"] = QJsonValue(QString("test"));

    // this will print "bar", because changing inner objects in the same document doesn't work!
    qDebug() << tmpObject["TestObject"].toObject()["TestObject2"].toObject()["FooAttribute"].toString();
    @

    ... when modifying inner object it doesn't change the document



  • As I also planned to use the Json-utilities in my next project, I tried to find some way to do this ... and it really seems that it is only possible to modify inner-data, by reassigning the outer objects after the modification ...

    here is some hacky utility function which does the magic:

    @
    void modifyJsonValue(QJsonObject& obj, const QString& path, const QJsonValue& newValue) {
    const int indexOfDot = path.indexOf('.');
    const QString propertyName = path.left(indexOfDot);
    const QString subPath = indexOfDot>0 ? path.mid(indexOfDot+1) : QString();

    QJsonValue subValue = obj[propertyName];

    if(subPath.isEmpty()) {
    subValue = newValue;
    }
    else {
    QJsonObject obj = subValue.toObject();
    modifyJsonValue(obj,subPath,newValue);
    subValue = obj;
    }

    obj[propertyName] = subValue;
    }
    void modifyJsonValue(QJsonDocument& doc, const QString& path, const QJsonValue& newValue) {
    QJsonObject obj = doc.object();
    modifyJsonValue(obj,path,newValue);
    doc = QJsonDocument(obj);
    }
    @

    Usage example ("tmpDoc" initialized like in my previous post):
    @modifyJsonValue(tmpDoc,"TestObject.TestObject2.FooAttribute",QString("test"));@

    ... this will modifier the Json so that the "test" is really set inside the Json-document



  • Hey man,

    Thank you for this snippet!

    I was just getting frustrated trying to work with QJson and you just saved my day!



  • Hello!

    I know this topic is really old, but i've seen it more then a year ago for the first time, and then G redirected me to it several times when i was struggling with read/write json in Qt.
    Code snippet provided by @sl-sy-ifm works really good on objects, but i needed to extend it to arrays as well, i've uploaded my code to github, feel free to use it anywhere ;)
    now modifyJsonValue takes QJsonValue as param, not QJsonObject so you can put there anything (on github, there are examples provided)
    https://github.com/rrozek/QtJsonModifyValue/blob/master/main.cpp

    oh, and maybe a snippet here as well:

    usage:
        modifyJsonValue(doc, "firstName", QJsonValue("Natalia"));
        modifyJsonValue(doc, "age", 22);
        modifyJsonValue(doc, "address.state", "None");
        modifyJsonValue(doc, "phoneNumber[0].number", "333 543-3210");
        modifyJsonValue(doc, "family[0][2]", "Bill");
        modifyJsonValue(doc, "family[1][1]", "Winston");
        modifyJsonValue(doc, "family[2].father.age", 56);
    
    void modifyJsonValue(QJsonValue& destValue, const QString& path, const QJsonValue& newValue)
    {
        const int indexOfDot = path.indexOf('.');
        const QString dotPropertyName = path.left(indexOfDot);
        const QString dotSubPath = indexOfDot > 0 ? path.mid(indexOfDot + 1) : QString();
    
        const int indexOfSquareBracketOpen = path.indexOf('[');
        const int indexOfSquareBracketClose = path.indexOf(']');
    
        const int arrayIndex = path.mid(indexOfSquareBracketOpen + 1, indexOfSquareBracketClose - indexOfSquareBracketOpen - 1).toInt();
    
        const QString squareBracketPropertyName = path.left(indexOfSquareBracketOpen);
        const QString squareBracketSubPath = indexOfSquareBracketClose > 0 ? (path.mid(indexOfSquareBracketClose + 1)[0] == '.' ? path.mid(indexOfSquareBracketClose + 2) : path.mid(indexOfSquareBracketClose + 1)) : QString();
    
        // determine what is first in path. dot or bracket
        bool useDot = true;
        if (indexOfDot >= 0) // there is a dot in path
        {
            if (indexOfSquareBracketOpen >= 0) // there is squarebracket in path
            {
                if (indexOfDot > indexOfSquareBracketOpen)
                    useDot = false;
                else
                    useDot = true;
            }
            else
                useDot = true;
        }
        else
        {
            if (indexOfSquareBracketOpen >= 0)
                useDot = false;
            else
                useDot = true; // acutally, id doesn't matter, both dot and square bracket don't exist
        }
    
        QString usedPropertyName = useDot ? dotPropertyName : squareBracketPropertyName;
        QString usedSubPath = useDot ? dotSubPath : squareBracketSubPath;
    
        QJsonValue subValue;
        if (destValue.isArray())
            subValue = destValue.toArray()[usedPropertyName.toInt()];
        else if (destValue.isObject())
            subValue = destValue.toObject()[usedPropertyName];
        else
            qDebug() << "oh, what should i do now with the following value?! " << destValue;
    
        if(usedSubPath.isEmpty())
        {
            subValue = newValue;
        }
        else
        {
            if (subValue.isArray())
            {
                QJsonArray arr = subValue.toArray();
                QJsonValue arrEntry = arr[arrayIndex];
                modifyJsonValue(arrEntry,usedSubPath,newValue);
                arr[arrayIndex] = arrEntry;
                subValue = arr;
            }
            else if (subValue.isObject())
                modifyJsonValue(subValue,usedSubPath,newValue);
            else
                subValue = newValue;
        }
    
        if (destValue.isArray())
        {
            QJsonArray arr = destValue.toArray();
            arr[arrayIndex] = subValue;
            destValue = arr;
        }
        else if (destValue.isObject())
        {
            QJsonObject obj = destValue.toObject();
            obj[usedPropertyName] = subValue;
            destValue = obj;
        }
        else
            destValue = newValue;
    }
    
    void modifyJsonValue(QJsonDocument& doc, const QString& path, const QJsonValue& newValue)
    {
        QJsonValue val;
        if (doc.isArray())
            val = doc.array();
        else
            val = doc.object();
    
        modifyJsonValue(val,path,newValue);
    
        if (val.isArray())
            doc = QJsonDocument(val.toArray());
        else
            doc = QJsonDocument(val.toObject());
    }
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.