QJsonObject partial path from variable - c++

I have a json object that I load:
QJsonObject json = CommonToolkit::Types::LoadJson(config);
Here is a partial of the json file content:
{
"config": {
"macos": {
"screen": {
"main": {
"height": 0,
"left": 0,
"top": 0,
"width": 0
}
},
"windows: {
}
What I do is check what os i'm running in. Using the following variable to store that os:
QString osPath;
In my test osPath = "macos"
So I want to update the applications geometry
QJsonObject jparam{
{ "height", value.height() },
{ "left", value.left() },
{ "width", value.width() },
{ "top", value.top() }
};
My problem is when I try to set jon with jparam using osPath:
json["config"][osPath]["screen"]["main"] = jparam;
The error I'm getting is:
/Users/adviner/Projects/Notes/src/Notes/data/config.cpp:87: error: type 'QJsonValueRef' does not provide a subscript operator
json["config"][osPath]["screen"]["main"] = jparam;
~~~~~~~~~~~~~~^~~~~~~

Editing a Json in Qt is not a simple task, in this case when using json["config"] you get a QJsonValueRef, this class is a helper to get other types of elements like QJsonObject, QJsonArray, int, QString, etc. by what to get the next element must be used toObject() but this value is a copy, so if you modify it, the initial element will not be modified, you have to reassign it.
QJsonObject json = CommonToolkit::Types::LoadJson(config);
QString osPath = "macos";
QJsonObject jparam{
{ "height", value.height() },
{ "left", value.left() },
{ "width", value.width() },
{ "top", value.top() }
};
// get objects
QJsonObject config_obj = json["config"].toObject();
QJsonObject osPath_obj = config_obj[osPath].toObject();
QJsonObject screen_obj = osPath_obj["screen"].toObject();
// modify param
screen_obj["main"] = jparam;
// set objects
osPath_obj["screen"] = screen_obj;
config_obj[osPath] = osPath_obj;
json["config"] = config_obj;

Related

How to delete nested json data using nlohmann c++

I have a below json data. I am using nlohmann json in C++.
{
"CompanyName": "XYZ Tech",
"Created": "2019-10-16T20:14:29Z",
"TxnId": "4509237",
"Tags": [
{
"ROIId": "Default",
"Time": 71,
"Tracker": "emp10"
},
{
"ROIId": "MC16",
"Time": 21,
"TrackerId": "emp10"
},
{
"ROIId": "Default",
"Time": 11,
"TrackerId": "emp11"
},
{
"ROIId": "MC18",
"Time": 10,
"TrackerId": "emp11"
}
],
"Type": "TxnData"
}
In above json data, inside Tags, we have data where the ROIId is Default. I want to delete it so that the data becomes:
{
"CompanyName": "XYZ Tech",
"Created": "2019-10-16T20:14:29Z",
"TxnId": "4509237",
"Tags": [
{
"ROIId": "MC16",
"Time": 21,
"TrackerId": "emp10"
},
{
"ROIId": "MC18",
"Time": 10,
"TrackerId": "emp11"
}
],
"Type": "TxnData"
}
How can I do it in c++. Thanks
I suggest iterating through the json::array stored in Tags and saving the Key of the matched elements. This way you can later validate the deletion and safely delete the elements.
Note that deleting is exactly like erasing with a STL vector - I prefer to delete from the end of the vector to avoid changing the keys while deleting multiple elements.
Here is a quick and dirty demo
And here is the code:
#include <iostream>
#include <vector>
#include "json3.6.1.hpp"
unsigned removeDefaultROIID(nlohmann::json& jsonObject, const std::string& value) {
std::vector<int> toremove;
//Loop through the `tags` json::array and create a vector of indexes to delete:
for (auto &it : jsonObject["Tags"].items()) {
//`.get<std::string>()` is the best way to make sure we are getting the value as std::string
if (it.value().at("ROIId").get<std::string>() == value)
toremove.push_back(stoi(it.key()));
}
//sort it before erase - we want to delete first biggest index:
std::sort(toremove.rbegin(), toremove.rend());
//delete using `.erase()` method:
for (int &it : toremove)
jsonObject["Tags"].erase(jsonObject["Tags"].begin() + it);
return toremove.size();
}
int main()
{
//Create the JSON object:
nlohmann::json jsonObject = R"({"CompanyName":"XYZ Tech","Created":"2019-10-16T20:14:29Z","TxnId":"4509237","Tags":[{"ROIId": "Default","Time": 71,"Tracker": "emp10"},{"ROIId":"MC16","Time": 21,"TrackerId": "emp10"},{"ROIId":"Default","Time":11,"TrackerId":"emp11"},{"ROIId":"MC18","Time": 10,"TrackerId":"emp11"}],"Type":"TxnData"})"_json;
std::cout << "JSON nested object value conditional erase:" << std::endl;
std::cout << "JSON object TAGS count - BEFORE deletion:" << jsonObject["Tags"].size() << std::endl;
//Call the method -> jlson is passed by ref
unsigned removed = removeDefaultROIID(jsonObject, "Default");
std::cout << "JSON object TAGS count - AFTER deletion:" << jsonObject["Tags"].size() << std::endl;
return 0;
}

Getting null values while saving json data in file using nlohmann json

I am using nlohmann json to create ans save some json values. But when I look at the file, I get null values in between the json. Below is how it looks like:
{
"Created": "2019-07-03T13:54:32Z",
"DataId": "BHI-201-U8",
"Data": [
{
"Name": "Andrew",
"Attended": "Yes"
},
{
"Name": "John",
"Attended": "Yes"
},
null, <-- unexpected null
{
"Name": "Ronny",
"Attended": "No"
},
null, <-- unexpected null
null, <-- unexpected null
{
"Name": "Mathew",
"Attended": "Yes"
}
],
"Type": "Person"
}
As you can see in the above json data, I am getting some unexpected null. Below is how I am saving it:
#include "nlohmann/json.hpp"
using nlohmann::json;
int main()
{
json outJson;
//some code
string outFile = "output.json";
for (const auto &data : trackedData->Detections())
{
//some code
outJson["Data"][data.id]["Name"] = data.name;
outJson["Data"][data.id]["Attended"] = data.status;
}
outJson["Created"] = created;
outJson["DataId"] = "BHI-201-U8";
outJson["Type"] = "Person";
std::ofstream output_file(outFile);
output_file << outJson.dump(4 , ' ', false);
output_file.close();
}
How can I remove these extra null from the code.
trackedData->Detections() returns a list of objects or structures and some of them are null-valued, hence nulls in the json. Try to do null-checking before adding data entry into json.
for (const auto &data : trackedData->Detections())
{
//some code
if (data != NULL)
{
outJson["Data"][data.id]["Name"] = data.name;
outJson["Data"][data.id]["Attended"] = data.status;
}
}

Qt JsonObjects and initializer lists -- deeper than one level?

This is in the documentation:
http://doc.qt.io/qt-5/qjsonobject.html#QJsonObject-1
Which shows that I can do:
QJsonObject o
{
{"x", 1},
{"y", 2}
}
...and that works, however
QJsonObject o
{
{"x", { "value", 1 } },
{"y", { "value", 2 } }
}
Does not compile. What am I missing?
You have to specify the type (QJsonObject or QJsonArray) in front of the initializer list {}, the compiler is unable to guess which one you want.
QJsonObject o
{
{"x", QJsonObject { {"key", "value"} }}
};
You could also try this approach, using C++11 raw string literal:
const QByteArray object = R"(
{
"x": { "value": "1" },
"y": { "value": "2" }
}
)";
QJsonObject jsonObject = QJsonDocument::fromJson(object).object();
qDebug() << jsonObject;

replacing fields from json formating using easier methodology in Qt 5.7 using QVariantMaps

I have 3 variantMaps created from JSON and I would like to substitute
for example all 3 things from 3d with 2nd and than second to first to the properties.
QVariantMap wholeMapToChange; //1.
QVariantMap propertiesMapToChange; //2.
QVariantMap cmdMap; //3.
1st contain this JSON data but in map:
{
properties {
"A": true,
"B": true,
"fieldName": "ewfqfqewf",
"C": false,
"fieldPassword": "451541611",
"isBtnSignOnClicked": true
},
type: "xyz"
}
2nd contain this JSON data but in map:
{
"A": true,
"B": true,
"fieldName": "ewfqfqewf",
"C": false,
"fieldPassword": "451541611",
"isBtnSignOnClicked": true
}
3d contain this JSON data but in map:
{
"fieldName": "nick",
"fieldPassword": "0000",
"isBtnSignOnClicked": true
}
What I see as a possibility for substituing 3 with 2 is to create cycle
for (QVariantMap::const_iterator it = propertiesMapToChange.begin(); it != propertiesMapToChange.end(); ++it){
for (QVariantMap::const_iterator itt = cmdMap.begin(); itt != cmdMap.end(); ++itt){
///here would be the comparig...
}
}
But I dont think this is good solution... I would like to ask you for advice or opinion, whether its correct, or there exist better way to do that.
Thx
It is the right solution if the maps aren't too big, since the cost is N*M. But it is a premature pessimization. You should implement the loop to have N+M cost: after all, the maps are sorted, so you only need to iterate each map once.
A complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/json-map-iter-39979440
#include <QtCore>
QVariantMap replaceMap(QVariantMap dst, const QVariantMap & src) {
auto dit = dst.begin();
auto sit = src.begin();
while (dit != dst.end() && sit != src.end()) {
if (sit.key() < dit.key()) {
++ sit;
continue;
}
if (dit.key() < sit.key()) {
++ dit;
continue;
}
Q_ASSERT(sit.key() == dit.key());
dit.value() = sit.value();
++ sit;
++ dit;
}
return dst;
}
int main() {
auto json1 = QJsonDocument::fromJson({R"ZZ({
"properties":{
"A":true,
"B":true,
"fieldName":"ewfqfqewf",
"C":false,
"fieldPassword":"451541611",
"isBtnSignOnClicked":true
},
"type":"xyz"
})ZZ"}).toVariant().value<QVariantMap>();
auto json2 = QJsonDocument::fromJson({R"ZZ({
"A":true,
"B":true,
"fieldName":"ewfqfqewf",
"C":false,
"fieldPassword":"451541611",
"isBtnSignOnClicked":true
})ZZ"}).toVariant().value<QVariantMap>();
auto json3 = QJsonDocument::fromJson(
{R"ZZ({
"fieldName":"nick",
"fieldPassword":"0000",
"isBtnSignOnClicked":true
})ZZ"}).toVariant().value<QVariantMap>();
json2 = replaceMap(json2, json3);
auto props = replaceMap(json1["properties"].value<QVariantMap>(), json2);
json1["properties"] = props;
qDebug() << QJsonDocument::fromVariant(json1).toJson().constData();
}
Output:
{
"properties": {
"A": true,
"B": true,
"C": false,
"fieldName": "nick",
"fieldPassword": "0000",
"isBtnSignOnClicked": true
},
"type": "xyz"
}

QT Reading a JSON file and storing and retriving values

I am trying to read a json file like shown below in QT. Can someone suggest a way to obtain values from json object and store them in separate containers or array such as test_cell2.CELLS[0] or some way so that nesting can also be taken care of and also I can access them easily after parsing the file
"test_cells2" : {
"CELLS" : {
"cell_0" : {
"prettyName" : "cell_1",
"CELLS" : {
"cell_1" : {
"prettyName" : "cell_1",
"type" : "default",
},
},
},
"cell_1" : {
"prettyName" : "cell_1",
"type" : "default",
},
"cell_2" : {
"type" : "text cell ko",
},
"cell_3" : {
"prettyName" : "cell_3",
"type" : "default",
},
"cell_4" : {
"data" : {
"settings" : {
"EXEC_PARAMETERS" : {
"defaultQueue" : "batch",
"environment" : {
"blabla" : "blabla2",
},
},
},
},
"type" : "parallel_test",
},
},
},
Take a look at this function
QJsonDocument::fromJson(QByteArray)
http://doc.qt.io/qt-5/qjsondocument.html#fromJson
Then use QJsonDocument::object() and you can use keys to get your values:
QJsonDocument doc = QJsonDocument::fromJson(QByteArray);
QJsonObject root = doc.object();
foreach(QJsonValue element, root["CELLS"].toArray()){
QJsonObject node = element.toObject();
node["whatEver"];
}
If you Qt Version > 5.5 check QJSonDocument, http://doc.qt.io/qt-5/qjsondocument.html.
An Example:
// Just read it from a file... or copy here
const QString yourJSON = "...";
const QJsonDocument jsonDoc = QJsonDocument::fromJson(yourJSON.toLocal8Bit());
const QVariantMap map = jsonDoc.toVariant().toMap();
To take care of nesting of CELLS inside CELLS, first load your CELLS array:
// Start read your parameter
const QVariant testCells2 = map["test_cells2"].toVariant();
const QVariantList CELLS = testCells2.toList();
// Now, we have a list available CELLS, check one be one
foreach (const QVariant& cell, CELLS) {
const QVariantMap wrapped = cell.toMap();
qDebug() << "Pretty Name: " << wrapped["prettyName"].toString();
qDebug() << "Type: " << wrapped["type"].toString();
const hasList = !wrapped["CELLS"].toList().isEmpty();
qDebug() << "Has child list? << hasList;
if (hasList) {
// Start another loop to check the child list.
}
}