How to read such a nested json array in Qt? - c++

Im trying to read json file with qt c++. After reading i will do some operations. I've read my json like it, but i want to print spesific datas, which include "float".
After finding them, i will assign a value each of these.
Thanks for any help
{
"A":[
{
"GPS":[
{
"GPS ID":[
"integer",
"0"
],
"GPS Mod":[
"integer",
"1"
],
"GPS Utc":[
"float",
"2"
],
"GPS Latitude":[
"float",
"3"
],
"GPS Longitude":[
"float",
"4"
]
}
]
Here is what I have tried up to now:
QByteArray data = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject root = doc.object();
QJsonArray tlmtArray = root.value("A").toArray();
for(int i=0; i<tlmtArray.size(); i++)
{
QJsonObject obj = tlmtArray.at(i).toObject();
}

Actually, you seem to already know what you need to do to handle Json data, you just have to continue to unpack your nested structures until the leaf data.
I'm not sure what was your issue, you just had to continue what you were doing but anyway, I have written an example that prints in the console the inner GPS data of the following structure (the one that you provided, I just added the missing braces and brackets):
{
"A":
[
{
"GPS":
[
{
"GPS ID":[
"integer",
"0"
],
"GPS Mod":[
"integer",
"1"
],
"GPS Utc":[
"float",
"2"
],
"GPS Latitude":[
"float",
"3"
],
"GPS Longitude":[
"float",
"4"
]
}
]
}
]
}
Here is the example function:
int printJsonData(const QByteArray & json_data)
{
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(json_data, &err);
if(err.error != QJsonParseError::NoError)
return -1; // Failure
QJsonObject root = doc.object();
QJsonArray tlmtArray = root.value("A").toArray();
for(int i = 0; i < tlmtArray.size(); ++i)
{
QJsonObject obj = tlmtArray[i].toObject();
QJsonArray gps_array = obj.value("GPS").toArray();
for(int j = 0; j < gps_array.size(); ++j)
{
QJsonObject gps_obj = gps_array[j].toObject();
for(QJsonObject::const_iterator cit = gps_obj.constBegin(); cit != gps_obj.constEnd(); ++cit)
{
std::cout << cit.key().toStdString() << ": (";
QJsonArray inner_data = cit.value().toArray();
for(int k = 0; k < inner_data.size(); ++k)
{
std::cout << inner_data[k].toString().toStdString() << (k < inner_data.size()-1 ? "," : "");
}
std::cout << ")\n";
}
}
}
return 0;
}
Output:
GPS ID: (integer,0)
GPS Latitude: (float,3)
GPS Longitude: (float,4)
GPS Mod: (integer,1)
GPS Utc: (float,2)
Please note that you really should use the methods QJsonObject::contains(), QJsonValue::isArray(), QJsonValue::isObject() and QJsonValue::isString() that Qt provides in order to check that the data you are extracting are exactly what you expect them to be. I didn't write such checks in the example because it is just an example and I wanted to avoid clutter and make the code less readable.

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 access element of JSON using Qt

I have this Json object and I want to access the "duration" and show it on console using Qt :
{
"kind": "youtube#videoListResponse",
"etag": "\"cbz3lIQ2N25AfwNr-BdxUVxJ_QY/brZ0pmrmXldPPKpGPRM-8I4dDFQ\"",
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#video",
"etag": "\"cbz3lIQ2N25AfwNr-BdxUVxJ_QY/PkTW6UN9MH0O2kDApjC3penIiKs\"",
"id": "WkC18w6Ys7Y",
"contentDetails": {
"duration": "PT58M21S",
"dimension": "2d",
"definition": "hd",
"caption": "false",
"licensedContent": true,
"projection": "rectangular"
}
}
]
}
And my Qt code is this :
{
QJsonDocument jsonResponse = QJsonDocument::fromJson(message);
results = jsonResponse.object();
QJsonValue v1 = results.value("items");
qDebug() << "v1 = " << v1;
QJsonValue v2 = v1.toObject().value("contentDetails");
qDebug() <<"v2 = " << v2;
QString v3 = v2.toObject().value("duration").toString();
qDebug() << "v3 = " << v3;
}
However my output is :
v1 = QJsonValue(array, QJsonArray([{"contentDetails":{"caption":"false","definition":"hd","dimension":"2d","duration":"PT58M21S","licensedContent":true,"projection":"rectangular"},"etag":"\"cbz3lIQ2N25AfwNr-BdxUVxJ_QY/PkTW6UN9MH0O2kDApjC3penIiKs\"","id":"WkC18w6Ys7Y","kind":"youtube#video"}]))
v2 = QJsonValue(undefined)
v3 = ""
So v1 is fine but v2 becomes undefined.What am I doing wrong and how can I access the "duration" item correctly?
The direct answer as follows:
// Read the file which has the JSON object.
QFile file("jsonString.json");
if(!file.open(QFile::ReadOnly)){
qDebug()<< "Error, Cannot open the file.";
return false;
}
QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
qDebug()<< jsonDoc.object().value("items").toArray()[0].toObject().value("contentDetails").toObject().value("duration").toString();
The result:
PT58M21S
items is a list, so calling toObject() on it just returns the default value. According to the documentation:
Converts the value to an object and returns it.
If type() is not Object, the defaultValue will be returned.
You need to be calling toArray() on it, which will convert it to a QJsonArray. From there you can take the first item from the array using a variety of methods, or iterate over the array if that makes more sense for your schema.

Accessing rapidjson::Document array by index

I have this code:
Document dataDoc;
dataDoc.SetArray();
Document::AllocatorType &allocator = dataDoc.GetAllocator();
for (size_t i = 0; i < 10; ++i)
{
ostringstream ss;
ss << "set_" << i + 1 << "_data";
Document doc;
doc.Parse<0>(UserDefault::getInstance()->getStringForKey(ss.str().c_str()).c_str());
dataDoc.PushBack(doc, allocator);
}
There's a rapidjson::Document "dataDoc" and I convert it to an array. Then I fill the array with Document objects that contain JSON objects that are fetched from cocos2d::UserDefault and parsed appropriately.
This is the JSON object that is added to the dataDoc:
{
"people": [
{
"name": "Peter",
"phone": "123",
"address": "Address 456"
},
{
"name": "Helen",
"phone": "123",
"address": "Address 456"
},
{
"name": "John",
"phone": "123",
"address": "Address 456"
}
]
}
Now the dataDoc array contains 10 of these objects.
I know that I can handle one object like this:
Document doc;
rapidjson::Value &people = doc["people"];
string name = people[0]["name"].GetString();
But how can I access, for example, the first object in the dataDoc array by index and get the name value as above?
Edit
Tried also with this code:
vector<string> jsons;
jsons.push_back("{\"people\":[{\"name\":\"Peter\",\"phone\":\"123\",\"address\":\"Address 456\"},{\"name\":\"Helen\",\"phone\":\"123\",\"address\":\"Address 456\"},{\"name\":\"John\",\"phone\":\"123\",\"address\":\"Address 456\"}]}");
jsons.push_back("{\"people\":[{\"name\":\"Peter\",\"phone\":\"123\",\"address\":\"Address 456\"},{\"name\":\"Helen\",\"phone\":\"123\",\"address\":\"Address 456\"},{\"name\":\"John\",\"phone\":\"123\",\"address\":\"Address 456\"}]}");
jsons.push_back("{\"people\":[{\"name\":\"Peter\",\"phone\":\"123\",\"address\":\"Address 456\"},{\"name\":\"Helen\",\"phone\":\"123\",\"address\":\"Address 456\"},{\"name\":\"John\",\"phone\":\"123\",\"address\":\"Address 456\"}]}");
Document dataDoc;
dataDoc.SetArray();
Document::AllocatorType &allocator = dataDoc.GetAllocator();
for (size_t i = 0; i < 3; ++i)
{
Document doc;
doc.Parse<0>(jsons.at(i).c_str());
dataDoc.PushBack(doc, allocator);
}
auto &people = dataDoc[0]["people"];
But it gave me the same error. It points to line 1688 in "document.h" located at ...\cocos2d\external\json\.
First, there seems to be an issue in your code the way you create doc - You need to provide the allocator of dataDoc:
Document doc(&allocator);
Then, rapidjson::Document inherits from rapidjson::Value, so you can simply treat it as a value and use the [] operator:
auto &people = dataDoc[0]["people"];
You can also iterate through the whole document:
for (auto &doc: dataDoc.getArray()) {
auto &people = doc["people"];
}
Prior to C++11:
for (Value::ConstValueIterator itr = dataDoc.Begin();
itr != dataDoc.End(); ++itr) {
auto &people = (*itr)["people"];
}

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

How to create/read/write JSON files in Qt5

Qt5 has a new JSON parser and I want to use it. The problem is that it isn't too clear about what the functions do in layman's terms and how to write code with it. That or I could be reading it wrong.
I want to know the code on creating a JSON file in Qt5 and what "encapsulates" mean.
Example: Read json from file
/* test.json */
{
"appDesc": {
"description": "SomeDescription",
"message": "SomeMessage"
},
"appName": {
"description": "Home",
"message": "Welcome",
"imp":["awesome","best","good"]
}
}
void readJson()
{
QString val;
QFile file;
file.setFileName("test.json");
file.open(QIODevice::ReadOnly | QIODevice::Text);
val = file.readAll();
file.close();
qWarning() << val;
QJsonDocument d = QJsonDocument::fromJson(val.toUtf8());
QJsonObject sett2 = d.object();
QJsonValue value = sett2.value(QString("appName"));
qWarning() << value;
QJsonObject item = value.toObject();
qWarning() << tr("QJsonObject of description: ") << item;
/* in case of string value get value and convert into string*/
qWarning() << tr("QJsonObject[appName] of description: ") << item["description"];
QJsonValue subobj = item["description"];
qWarning() << subobj.toString();
/* in case of array get array and convert into string*/
qWarning() << tr("QJsonObject[appName] of value: ") << item["imp"];
QJsonArray test = item["imp"].toArray();
qWarning() << test[1].toString();
}
OUTPUT
QJsonValue(object, QJsonObject({"description": "Home","imp": ["awesome","best","good"],"message": "YouTube"}) )
"QJsonObject of description: " QJsonObject({"description": "Home","imp": ["awesome","best","good"],"message": "YouTube"})
"QJsonObject[appName] of description: " QJsonValue(string, "Home")
"Home"
"QJsonObject[appName] of value: " QJsonValue(array, QJsonArray(["awesome","best","good"]) )
"best"
Example: Read json from string
Assign json to string as below and use the readJson() function shown before:
val =
' {
"appDesc": {
"description": "SomeDescription",
"message": "SomeMessage"
},
"appName": {
"description": "Home",
"message": "Welcome",
"imp":["awesome","best","good"]
}
}';
OUTPUT
QJsonValue(object, QJsonObject({"description": "Home","imp": ["awesome","best","good"],"message": "YouTube"}) )
"QJsonObject of description: " QJsonObject({"description": "Home","imp": ["awesome","best","good"],"message": "YouTube"})
"QJsonObject[appName] of description: " QJsonValue(string, "Home")
"Home"
"QJsonObject[appName] of value: " QJsonValue(array, QJsonArray(["awesome","best","good"]) )
"best"
JSON under QT is actually quite pleasant - I was surprised. This is an example of how to create a JSON output with some structure.
Forgive me for not explaining what the fields all mean - it is a Ham Radio processing output script.
This is the QT C++ Code
void CabrilloReader::JsonOutputMapper()
{
QFile file(QDir::homePath() + "/1.json");
if(!file.open(QIODevice::ReadWrite)) {
qDebug() << "File open error";
} else {
qDebug() <<"JSONTest2 File open!";
}
// Clear the original content in the file
file.resize(0);
// Add a value using QJsonArray and write to the file
QJsonArray jsonArray;
for(int i = 0; i < 10; i++) {
QJsonObject jsonObject;
CabrilloRecord *rec= QSOs.at(i);
jsonObject.insert("Date", rec->getWhen().toString());
jsonObject.insert("Band", rec->getBand().toStr());
QJsonObject jsonSenderLatObject;
jsonSenderLatObject.insert("Lat",rec->getSender()->fLat);
jsonSenderLatObject.insert("Lon",rec->getSender()->fLon);
jsonSenderLatObject.insert("Sender",rec->getSender_call());
QJsonObject jsonReceiverLatObject;
jsonReceiverLatObject.insert("Lat",rec->getReceiver()->fLat);
jsonReceiverLatObject.insert("Lon",rec->getReceiver()->fLon);
jsonReceiverLatObject.insert("Receiver",rec->getReceiver_call());
jsonObject.insert("Receiver",jsonReceiverLatObject);
jsonObject.insert("Sender",jsonSenderLatObject);
jsonArray.append(jsonObject);
QThread::sleep(2);
}
QJsonObject jsonObject;
jsonObject.insert("number", jsonArray.size());
jsonArray.append(jsonObject);
QJsonDocument jsonDoc;
jsonDoc.setArray(jsonArray);
file.write(jsonDoc.toJson());
file.close();
qDebug() << "Write to file";
}
It takes an internal QT Structure (a List of pointers to a CabrilloRecord object ... which you just ignore) and extracts some fields. These fields are then output in a nested JSON format which looks like this.
[
{
"Band": "20",
"Date": "Sat Jul 10 12:00:00 2021",
"Receiver": {
"Lat": 36.400001525878906,
"Lon": 138.3800048828125,
"Receiver": "8N3HQ "
},
"Sender": {
"Lat": 13,
"Lon": 122,
"Sender": "DX0HQ "
}
},
{
"Band": "20",
"Date": "Sat Jul 10 12:01:00 2021",
"Receiver": {
"Lat": 36.400001525878906,
"Lon": 138.3800048828125,
"Receiver": "JA1CJP "
},
"Sender": {
"Lat": 13,
"Lon": 122,
"Sender": "DX0HQ "
}
}]
I hope this speeds up someone else's progression on this topic.
Sadly, many JSON C++ libraries have APIs that are non trivial to use, while JSON was intended to be easy to use.
So I tried jsoncpp from the gSOAP tools on the JSON doc shown in one of the answers above and this is the code generated with jsoncpp to construct a JSON object in C++ which is then written in JSON format to std::cout:
value x(ctx);
x["appDesc"]["description"] = "SomeDescription";
x["appDesc"]["message"] = "SomeMessage";
x["appName"]["description"] = "Home";
x["appName"]["message"] = "Welcome";
x["appName"]["imp"][0] = "awesome";
x["appName"]["imp"][1] = "best";
x["appName"]["imp"][2] = "good";
std::cout << x << std::endl;
and this is the code generated by jsoncpp to parse JSON from std::cin and extract its values (replace USE_VAL as needed):
value x(ctx);
std::cin >> x;
if (x.soap->error)
exit(EXIT_FAILURE); // error parsing JSON
#define USE_VAL(path, val) std::cout << path << " = " << val << std::endl
if (x.has("appDesc"))
{
if (x["appDesc"].has("description"))
USE_VAL("$.appDesc.description", x["appDesc"]["description"]);
if (x["appDesc"].has("message"))
USE_VAL("$.appDesc.message", x["appDesc"]["message"]);
}
if (x.has("appName"))
{
if (x["appName"].has("description"))
USE_VAL("$.appName.description", x["appName"]["description"]);
if (x["appName"].has("message"))
USE_VAL("$.appName.message", x["appName"]["message"]);
if (x["appName"].has("imp"))
{
for (int i2 = 0; i2 < x["appName"]["imp"].size(); i2++)
USE_VAL("$.appName.imp[]", x["appName"]["imp"][i2]);
}
}
This code uses the JSON C++ API of gSOAP 2.8.28. I don't expect people to change libraries, but I think this comparison helps to put JSON C++ libraries in perspective.
An example on how to use that would be great. There is a couple of examples at the Qt forum, but you're right that the official documentation should be expanded.
QJsonDocument on its own indeed doesn't produce anything, you will have to add the data to it. That's done through the QJsonObject, QJsonArray and QJsonValue classes. The top-level item needs to be either an array or an object (because 1 is not a valid json document, while {foo: 1} is.)