read JSON line by line with QT [duplicate] - c++

This question already has answers here:
Foreach Through QJsonObject to get Key/Value Pair
(2 answers)
Closed 4 years ago.
I have a JSON like this:
{
"09:32 24.09.2018": "456",
"09:56 24.09.2018": "854",
"09:57 24.09.2018": "854",
"09:59 24.09.2018": "853",
"10:00 24.09.2018": "369",
"10:01 24.09.2018": "369"
}
How can I read this line by line in QT5. I would like to write it in a QTableWidget to have "09:32 24.09.2018" in first row / first column, "456" in first row / second column, "09:56 24.09.2018" second row / first column and so on..
My current loading works like this:
QJsonDocument getFile = loadJson("test");
QJsonObject obj = getFile.object();
with:
QJsonDocument MainWindow::loadJson(QString fileName) {
QFile jsonFile(fileName);
jsonFile.open(QFile::ReadOnly);
return QJsonDocument().fromJson(jsonFile.readAll());
}
Thank you.

you can iterate over the QJsonObject to transverse it:
QString val;
QFile file;
//set the file name
file.setFileName("foo.json");
//open the file name
file.open(QIODevice::ReadOnly | QIODevice::Text);
//read the file name
val = file.readAll();
//close the file name
file.close();
//string to json doc
QJsonDocument doc = QJsonDocument::fromJson(val.toUtf8());
//json doc to json object
QJsonObject object = doc.object();
//json object can be iterated
for (auto it = object.begin(), end=object.end(); it != end; ++it)
{
qDebug() << "Key: " << it.key() << "Val: " << it.value();
}

Related

How to parse QJsonArray?

In my QT application, there is a need to store the value of structures of the same type in a JSON dictionary.
I know about the [JSON Save Game Example article][1], I tried to figure it out for a long time, I didn’t come to anything, I also surfed the forums with the same result.
The main problem is this:
I have a JSON document:
{
"devices": [
{
"name": "some name",
"price": 2000,
"year": 2022
}
]
}
I use the following code to read the information:
//open my JSON
QFile file("path/to/myfile.json");
file.open(QIODevice::ReadOnly);
QByteArray jsonData = file.readAll();
//finding array
QJsonDocument document = QJsonDocument::fromJson(jsonData);
QJsonObject object = document.object();
QJsonArray temp_array = object["devices"].toArray();
//reading
qDebug() << temp_array[0].toObject().value("name").toString(); //returned ""
qDebug() << temp_array.size(); //returned 0
qDebug() << temp_array.empty(); //returned true
qDebug() << object.keys(); //returned QList("devices")
As I previously pointed out in a comment, trying to read the values of the "name" key returned me an empty string, the size and empty functions indicate that I'm looking at an empty array. However, the keys function indicates that my json object still contains the "devices" key.
What could be the problem?
[1]: https://doc.qt.io/qt-5/qtcore-serialization-savegame-example.html
Your code seems to be doing correct things in correct order but something goes wrong. Either your file is not found or it doesn't get opened, or it is found but parsing fails.
You would find out if you did some checks instead of just assuming things work.
Below you will find example of robust programming. The example code checks
if file exists
if file opening fails
if file parsing to JSON fails
if file is actually JSON object (and not e.g. JSON array)
if size of the JSON array named "devices" is bigger than zero
if object in JSON array contains "name" property
if "name" property type is string
(I didn't try to compile the code, but I'm sure you get the idea and figure out what went wrong. If you made this kind of code to a function you would either return or throw exception when things go wrong...)
QFile file("path/to/myfile.json");
if (!file.exists()) {
qWarning() << "File doesn't exist";
}
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Couldn't open file";
}
QByteArray jsonData = file.readAll();
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(jsonData, &parseError));
if (parseError.error != QJsonParseError::NoError) {
qWarning() << "Invalid json file. Parsing failed:" << parseError.error << parseError.errorString();
}
if (!document.isObject()) {
qWarning() << "File is not JSON object!";
}
const QJsonObject &object = document.object();
QJsonArray temp_array = object["devices"].toArray();
if (temp_array.size() < 1) {
qWarning() << "JSON array object count:" << temp_array.size();
}
QJsonObject jsonObj = temp_array[0].toObject();
if (jsonObj.contains("name")) {
if (jsonObj["name"].isString()) {
qDebug() << jsonObj["name"].toString();
} else {
qWarning() << "name" << "is not string!";
}
} else {
qWarning() << "Object in JSON array did not have" << "name";
}

How to add object to Json file

I am trying to log data from 3 sensors to a json file. All I want to be able to accomplish is to write the Speed, Latitude and Longitude to a Json file, with an object containing each of the above. That is a json file that contains one route object, n sub objects each of which contain speed, latitude, longitude.
These 3 values I get from 3 global QList lists. Below is the json file which is stored locally. (The double values are not actual values, just for testing purposes)
{
"Sensordata": [
{
"Speed": 1,
"GPSLat":-12.5687,
"GPSLong":26.125546
},
{
"Speed": 1,
"GPSLat":-12.5687,
"GPSLong":26.125546
}
]
}
This is what the json must look like and when I add it must be formatted in the same way
void MainWindow::save_to_json() {
QFile file_obj(".../SensorData.json");
if(!file_obj.open(QIODevice::ReadOnly)){
qDebug()<<"Failed to open "<<"SensorData.json";
exit(1);
}
QTextStream file_text(&file_obj);
QString json_string;
json_string = file_text.readAll();
file_obj.close();
QByteArray data_json = json_string.toLocal8Bit();
QJsonDocument doc = QJsonDocument::fromJson(data_json);
QJsonObject rootObj = doc.object();
QJsonValue SensorData = rootObj.value("SensorData");
if(!SensorData.isArray())
{
// array expected - handle error
}
QJsonArray SensorDataArray = SensorData.toArray();
QJsonObject newObject;
newObject["Speed"] = speed_array.takeFirst();
newObject["GPSLat"] = gps_lat.takeFirst();
newObject["GPSLong"] = gps_long.takeFirst();
SensorDataArray.push_back(newObject);
}
ASSERT: "!isEmpty()" in file /home/username/Qt/5.12.1/gcc_64/include /QtCore/qlist.h, line 347
11:32:55: The program has unexpectedly finished.
11:32:55: The process was ended forcefully.
This is the error the above code creates.
To modify the data, given your example, you need to check if the contained data in the QJsonDocument is an array or a simple object. In your case, I suppose you want to append data to an array. Try something like this:
// Read the data
const QString filename = "example.json";
QJsonDocument doc = read(filename);
// Check that it's an array and append new data
QJsonValue sensorData = doc.value("SensorData");
if (!sensorData.isArray()) {
// if the doc is empty you may want to create it
}
// Get the array and insert the data
auto array = sensorData.array();
array.append(QJsonObject{
{"Speed", speed_array.takeFirst()},
{"GPSLat", gps_lat.takeFirst()},
{"GPSLong",gps_long.takeFirst(),
});
// Restore your sensor data
doc.setObject(QJsonObject{{"SensorData", array}});
// Write the new data
write(filename, doc);
A helper functions to read/write JSON documents may avoid the mistake of open/closing a file:
QJsonDocument read(const QString& filename) {
QFile file(filename);
file.open(QIODevice::ReadOnly | QIODevice::Text);
const QString val = file.readAll();
file.close();
return QJsonDocument::fromJson(val.toUtf8());
}
void write(const QString& filename, const QJsonDocument& document) {
QFile file(filename);
file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
file.write(document.toJson());
file.close();
}
Updates
To not overwrite the original doc, you must update the field of the root object or use QJsonValueRef.
// Get a reference to your array
QJsonObject root = doc.object();
QJsonValueRef ref = root.find("SensorData").value();
// get the array and insert the data
QJsonArray array = ref.toArray();
array.append(QJsonObject{
{"Speed", speed_array.takeFirst()},
{"GPSLat", gps_lat.takeFirst()},
{"GPSLong",gps_long.takeFirst(),
});
// Update the ref with the new data
ref = array
// update the modified data in the json document
doc.setObject(root);

Serializing/parsing multiple objects in one file in Qt C++

I need to serialize and parse multiple objects from my project, in order to save/load them when needed.
My objects will have exactly the same components : a QString name, an integer id, a QString description, and two integer x, y.
I'll need something like this :
{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
So I'll build my QJsonObject like this :
QJsonObject json;
json["id"] = object_to_serialize.get_id();
json["name"] = object_to_serialize.get_name();
json["description"] = object_to_serialize.get_description();
json["x"] = object_to_serialize.get_x();
json["y"] = object_to_serialize.get_y();
QJsonDocument filedoc(json);
file.write(filedoc.toJson);`
And in the file it will appear like this :
{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}
{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}
My serialiser will take in parameter the object, the savefile name, and transform the object into a QJsonObject. It will need then to read the file to check if an object with the same id is here. If it is here, it will need to replace it, and if it is not, it will append it.
I'm a little lost between my serialization options and how to read it ;
Should I make a QJsonArray with multiple QJsonObject inside or QJsonObject with QJsonArrays ?
When I read it, I will need to check for the id ; but will a
foreach(object.value["id"] == 42)
//create the QJsonObject from the one with 42 and change it with the new data
will do to parse the object and not all of them ? Is there a better way ?
Thank you in advance for your answers.
You can have an array of json object, each of them having an ID so you can parse the relevant ones.
Although you could also parse all of them and add them in a map, as long as you don't have very heavy files it should be fine.
void parseJson(const QString &data)
{
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
if (doc.isNull())
{
war("invalid json document");
return;
}
QJsonArray jsonArray = doc.array();
foreach (const QJsonValue & value, jsonArray) {
QJsonObject obj = value.toObject();
if (obj.contains("id"))
{
if (obj["id"].toInt() == yourId) parseObject(obj);
}
}
}
void parseObject(const QJsonObject &obj)
{
if (obj.contains("valueA")) valueA = obj["valueA"].toDouble();
if (obj.contains("valueB")) valueB = obj["valueB"].toDouble();
}
This will work just fine if your file is not too big
Bigger Files
Now if you have very large file, it might be an issue to load it all in memory and parse it.
Since your structure is always the same and quite simple, JSON might not be the best choice, one more efficient method would be to do your own parser (or use probably some existing ones) that could read the file and process it as a stream.
Another method, would be to have one JSON entry per line preceded by an ID with a fixed number of digit. Load this in a QHash lookup and then only read id of interest from the file and only parse a small section.
// This code is not tested and is just to show the principle.
#define IDSIZE 5
QHash<int64, int64> m_lookup; // has to be global var
// For very large file, this might take some time and can be done on a separate thread.
// it needs to be done only once at startup (given the file is not modified externally)
void createLookup(const QString &fileName)
{
QFile inputFile(fileName);
if (inputFile.open(QIODevice::ReadOnly))
{
QTextStream in(&inputFile);
while (!in.atEnd())
{
int position = in.pos(); // store the position in the file
QString line = in.readLine();
int id = line.mid(0,IDSIZE).toInt(); // 5 digit id (like 00001, 00002, etc...
m_lookup[id] = position + IDSIZE;
}
inputFile.close();
}
}
QString getEntry(const QString &fileName, int64 id)
{
if (m_lookup.contains(id))
{
QFile inputFile(fileName);
if (inputFile.open(QIODevice::ReadOnly))
{
inputFile.seek(m_lookup[id]);
QString data = inputFile.readLine();
inputFile.close();
return data;
} else {
return QString(); // or handle error
}
} else {
return QString(); // or handle error
}
}
// use example
QString data = getEntry(id);
if (data.length() > 0)
{
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
if (!doc.isNull())
{
// assign your variables
}
}
and your data file looking like this:
00042{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
00044{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}
00046{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}
The advantage of this method, it will only read the entry of interest, and avoid having to load MB or GB of data in memory just to get a specific entry.
This could further be improved with a lookup table stored at the beginning of the file.

Why can't I parse the Cryptsy JSON API with Qt5/C++?

I'm trying to parse this JSON Web-API using Qt5 and C++ using QJsonDocument and QJsonObject as seen here. But I fail to access the JSON value of the QJsonObject.
This is what I've tried so far:
// Contains the whole API as QString...
QString data = QString(reply->readAll());
// Reads the JSON as QJsonDocument...
QJsonDocument jsonResponse = QJsonDocument::fromJson(data.toUtf8());
// Reads the JSON as QJsonObject...
QJsonObject jsonObject = jsonResponse.object();
Now I have my object well prepared, but trying to access the values of the JSON somehow fails:
// This returns an empty string ""!?!
qDebug() << jsonObject.value("success").toString();
Well, maybe I got the keys wrong:
// Let's check the keys...
QStringList stringList = jsonObject.keys();
for (QStringList::Iterator it = stringList.begin(); it != stringList.end(); ++it)
{
// This returns "success" and "return" - huh!?!
qDebug() << *it;
}
OK, the keys are veryfied, why is it not working?
// Let's check the values by using the keys directly...
for (QStringList::Iterator it = stringList.begin(); it != stringList.end(); ++it)
{
// This returns empty strings "" and "" - now what?!?
qDebug() << jsonObject.value(*it).toString();
}
This again, makes no sense at all. I can't see why I can not access the value of the JSON object by the keys. Any idea?
I tried exactly the same code on other JSON APIs (for example this one) without any issues. I am totally stuck here.
Here's my solution for Qt5 Json parsing the Cryptsy API.
QEventLoop loopEvent;
QNetworkAccessManager namMNGR;
QObject::connect(&namMNGR, SIGNAL(finished(QNetworkReply*)), &loopEvent, SLOT(quit()));
QNetworkRequest req(QUrl(QString("http://pubapi.cryptsy.com/api.php?method=singlemarketdata&marketid=%1").arg(marketID)));
QNetworkReply *reply = namMNGR.get(req);
loopEvent.exec();
//Json API parsing begins.
QString jsonSTR = reply->readAll();
if (!(reply->error() == QNetworkReply::NoError)) {
delete reply; //API Connection Problem.
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonSTR.toUtf8());
QJsonObject obj1 = jsonDocument.object();
QJsonValue val1 = obj1.value(obj1.keys().first());
QJsonObject obj2 = val1.toObject();
QJsonValue val2 = obj2.value(obj2.keys().first());
QJsonObject obj3 = val2.toObject();
QJsonValue marketDataValue = obj3.value(obj3.keys().first());
QJsonObject marketDataObject = marketDataValue.toObject();
QJsonArray sellordersArray = marketDataObject["sellorders"].toArray();
Have you managed to get Authenticated POST API data from Qt5? I'm trying to figure out how to do it.

change the name file putting the first line of my file C++ & Qt

I want to change the name of my file but I don't know how to do it. I have a .txt with words in two lines,and like to take the first line same as name of my file .txt.
This is my code:
void ventana1::on_ButtonGuardar_clicked()
{
QDir directory("C:/Users/Jaime/Desktop/interfaz/pacientes");
QString mFilename = directory.filePath("paciente.txt");
QFile sFile(mFilename);
if(sFile.open(QFile::WriteOnly | QFile::Text))
{
QTextStream out(&sFile);
out << ui.lineEdit_2->text()<< "\n"
<< ui.lineEdit->text();
sFile.flush();
sFile.close();
}
}
Simply replace
QString mFilename = directory.filePath("paciente.txt");
by
QString mFilename = directory.filePath(ui.lineEdit_2->text());
if you need to have the content of your lineEdit as your filename.