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

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;

Related

Qt C++ Parsing JSON nested data from API response using JsonDocument

I have the following JSON response read from an API, using QJsonDocument, and I'm trying to read the postal codes value:
{
"data": {
"website": "https://somesite.com/",
"name": "SomeSite",
"description": "description of the product",
"techs": [
"sitetech1",
"sitetech2"
],
"state": null,
"city": null,
"postal codes": [
{
"value": "123456",
}
]
}
}
With the following code I read the response:
QStringList postals;
QString jsonText = (QString)reply->readAll();
QJsonDocument jsonResponse = QJsonDocument::fromJson(jsonText.toUtf8());
QJsonObject jsonObject = jsonResponse.object();
QJsonArray jsonArray = jsonObject["data"].toArray();
if(jsonArray.isEmpty()) {
qDebug() << "jsonArray is empty"; // this gets to here.
return "empty";
}
foreach (const QJsonValue & value, jsonArray) {
QJsonObject obj = value.toObject();
QJsonArray arr = obj["postal codes"].toArray();
foreach(const QJsonValue & value, arr) {
postals.append(obj["value"].toString());
}
}
}
But it seems like the jsonArray is empty. Did I parse the JSON incorrectly?
"data" does not seem to be an array, so "toArray()" fails. Also, in the loop, you are looking for the key "value" inside the wrong object it seems. Also, there is a comma which shouldn't be there in the json.
Something like this maybe?
{
"data": [
{
"website": "https://somesite.com/",
"name": "SomeSite",
"description": "description of the product",
"techs": [
"sitetech1",
"sitetech2"
],
"state": null,
"city": null,
"postal codes": [
{
"value": "123456"
}
]
}
]
}
code:
QString jsonText = f.readAll();
QJsonParseError error;
QJsonDocument jsonResponse = QJsonDocument::fromJson(jsonText.toUtf8(), &error);
if (error.error != QJsonParseError::NoError)
qDebug() << "Error:" << error.errorString();
QJsonObject jsonObject = jsonResponse.object();
QJsonArray jsonArray = jsonObject["data"].toArray();
if (jsonArray.isEmpty()) {
qDebug() << "jsonArray is empty"; // this gets to here.
return -1;
}
for (const QJsonValue & value : jsonArray) {
QJsonObject obj = value.toObject();
QJsonArray arr = obj["postal codes"].toArray();
for (const QJsonValue & value : arr) {
postals.append(value["value"].toString());
qDebug() << "val:" << value["value"].toString();
}
}
output:
val: "123456"

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;
}

QJsonObject partial path from variable

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;

How do I create JSON array using QT

I want store my datas in JSON file like:
{
"plottingData": [
{
"min": 17,
"max": 35,
"mean": 20
},
{
"min": 7,
"max": 35,
"mean": 17
},
{
"min": 8,
"max": 50,
"mean": 29
}
]
}
How can I create this struct?
I used to QJsonObject but I couldn't add QJsonArray like this.
From Qt documentation:
QJsonArray plot_array;
// use initializer list to construct QJsonObject
auto data1 = QJsonObject(
{
qMakePair(QString("min"), QJsonValue(17)),
qMakePair(QString("max"), QJsonValue(35)),
qMakePair(QString("mean"), QJsonValue(20))
});
plot_array.push_back(QJsonValue(data1));
// Or use insert method to create your QJsonObject
QString min_str("min");
QString max_str("max");
QString mean_str("mean");
for(auto item : your_collection)
{
QJsonObject item_data;
item_data.insert(min_str, QJsonValue(item.min));
item_data.insert(max_str, QJsonValue(item.max));
item_data.insert(mean_str, QJsonValue(item.mean));
plot_array.push_back(QJsonValue(item_data));
}
QJsonObject final_object;
final_object.insert(QString("plottingData"), QJsonValue(plot_array));
QJsonObject o1
{
{ "min", 17 },
{ "max", 35 },
{ "mean", 20 },
};
QJsonObject o2;
o2 [ "min" ] = 7;
o2 [ "max" ] = 35;
o2 [ "mean"] = 17;
QJsonArray arr;
arr.append ( o1 );
arr.append ( o2 );
QJsonObject obj;
obj [ "plottingData" ] = arr;
//to see the JSON output
QJsonDocument doc ( obj );
qDebug() << doc.toJson ( QJsonDocument::Compact ); // or QJsonDocument::Indented
// output: "{\"plottingData\":[{\"max\":35,\"mean\":20,\"min\":17},{\"max\":35,\"mean\":17,\"min\":7}]}"

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"
}