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.cppoh, 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()); }
-
Here is an improved implementation:
https://github.com/heksbeks/qjsonpath -
This post is deleted!