Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Modify nested QJsonValue
Forum Updated to NodeBB v4.3 + New Features

Modify nested QJsonValue

Scheduled Pinned Locked Moved General and Desktop
8 Posts 7 Posters 12.8k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    Donner
    wrote on last edited by
    #1

    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.

    Christian EhrlicherC 1 Reply Last reply
    0
    • B Offline
      B Offline
      b1gsnak3
      wrote on last edited by
      #2

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

      1 Reply Last reply
      0
      • S Offline
        S Offline
        sl.sy.ifm
        wrote on last edited by
        #3

        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

        1 Reply Last reply
        0
        • S Offline
          S Offline
          sl.sy.ifm
          wrote on last edited by
          #4

          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

          1 Reply Last reply
          2
          • A Offline
            A Offline
            andresantos
            wrote on last edited by
            #5

            Hey man,

            Thank you for this snippet!

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

            1 Reply Last reply
            0
            • R Offline
              R Offline
              rrozek
              wrote on last edited by
              #6

              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());
              }
              
              1 Reply Last reply
              1
              • H Offline
                H Offline
                heksbeks
                wrote on last edited by
                #7

                Here is an improved implementation:
                https://github.com/heksbeks/qjsonpath

                1 Reply Last reply
                0
                • D Donner

                  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.

                  Christian EhrlicherC Offline
                  Christian EhrlicherC Offline
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8
                  This post is deleted!
                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved