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 11 Mar 2013, 18:12 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.

    C 1 Reply Last reply 19 Jun 2023, 19:45
    0
    • B Offline
      B Offline
      b1gsnak3
      wrote on 12 Mar 2013, 07:15 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 12 Mar 2013, 13:57 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 12 Mar 2013, 15:32 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 25 Apr 2015, 14:48 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 12 May 2017, 00:13 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 19 Jun 2023, 19:44 last edited by
                #7

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

                1 Reply Last reply
                0
                • D Donner
                  11 Mar 2013, 18:12

                  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.

                  C Offline
                  C Offline
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on 19 Jun 2023, 19:45 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