Parsing JSON starting with bracket `[` with Qt5 - c++

According to this, A JSON starting with bracket is valid, so i have encoded a list of item, in a test.json file:
[{"name": "a"},{"name": "b"}]
Heavily inspirated by this answer, i push this code in main.cpp:
#include <QApplication>
#include <QFile>
#include <QByteArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QVariant>
#include <QDebug>
#include <iostream>
int main(int argc, char *argv[]) {
// Reading the JSON, parse it, get data as QJsonObject
QString val;
QFile file;
file.setFileName("test.json");
file.open(QIODevice::ReadOnly | QIODevice::Text);
val = file.readAll();
file.close();
QJsonDocument d = QJsonDocument::fromJson(val.toUtf8());
QJsonObject sett2 = d.object();
// Printings
qWarning() << "File content: " << val;
qWarning() << "sett2: " << sett2 << " (empty: " << sett2.empty() << ')';
// try to access the data directly
QJsonValue value = sett2.value(QString("name"));
qWarning() << "name value: " << value;
QJsonObject item = value.toObject();
qWarning() << "QJsonObject of accessed value: " << item;
}
Here is the output:
File content: "[{\"name\": \"a\"},{\"name\": \"b\"}]\n"
sett2: QJsonObject() (empty: true )
name value: QJsonValue(undefined)
QJsonObject of accessed value: QJsonObject()
We see that the file is properly readed. However, no data seems to be accessed : sett2 is empty, as if holding no data.
After searching on the QJsonObject documentation, i can't found any routine that can give access to the data in the file, in this case: the only one that seems to allow an access to fields is the value(), method, but it needs a parameter.
Call it with 0, 1, NULL "name", "a", "b" and "knock knock" lead to compilation error or empty data.
Other methods, like keys(), returns also empty data.
How access data of objects ? (here, name: "a" and name: "b")

The answere is simple - you have to call QJsonDocument::array() instead of object:
QJsonArray sett2 = d.array();

Related

Qt cannot write to file. File exists, isn't open, "Unknown error" (Windows OS)

Would really appreciate your help. I'm new to Qt and C++.
I managed to get this working previously, but for some reason am no longer able to do so. I haven't touched anything to do with the writing of files, but all functions pertaining to file writing no longer seem to function. Here's an example of one of the simpler write functions:
#include <QStringList>
#include <QDir>
#include <QFile>
#include <QString>
#include <QTextStream>
void HandleCSV::writeToPIDCSV(UserAccount newUser)
{
// Storing in userPID db
QString filePath = returnCSVFilePath("dbPID");
qDebug() << "File path passsed to writeToPIDCSV is " << filePath;
// Open CSV filepath retrieved from associated dbName
QFile file(filePath);
if (!file.open(QIODevice::ReadWrite | QIODevice::Append))
{
qDebug() << file.isOpen() << "error " << file.errorString();
qDebug() << "File exists? " << file.exists();
qDebug() << "Error message: " << file.error();
qDebug() << "Permissions err: " << file.PermissionsError;
qDebug() << "Read error: " << file.ReadError;
qDebug() << "Permissions before: " << file.permissions();
// I tried setting permissions in case that was the issue, but there was no change
file.setPermissions(QFile::WriteOther);
qDebug() << "Permissions after: " << file.permissions();
}
// if (file.open(QIODevice::ReadWrite | QIODevice::Append))
else
{
qDebug() << "Is the file open?" << file.isOpen();
// Streaming info back into db
QTextStream stream(&file);
stream << newUser.getUID() << "," << newUser.getEmail() << "," << newUser.getPassword() << "\n";
}
file.close();
}
This gets me the following output when run:
File path passsed to writeToPIDCSV is ":/database/dummyPID.csv"
false error "Unknown error"
File exists? true
Error message: 5
Permissions err: 13
Read error: 1
Permissions before: QFlags(0x4|0x40|0x400|0x4000)
Permissions after: QFlags(0x4|0x40|0x400|0x4000)
The file clearly exists and is recognised when run, but for some reason file.isOpen() is false, and the permissions show that the user only has read (not write) permissions that are unaffected by permission settings.
Would someone have a clue as to why this is happening?
Many thanks in advance
Update
Thanks #chehrlic and #drescherjm - I didn't realise that I can only read but not write to my resource file!
Using QStandardPaths allows me to write to my AppDataLocation. I've included the code below for others who might encounter similar issues, adapted from https://stackoverflow.com/a/32535544/9312019
#include <QStringList>
#include <QDir>
#include <QFile>
#include <QString>
#include <QTextStream>
#include <QStandardPaths>
void HandleCSV::writeToPIDCSV(UserAccount newUser)
{
auto path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (path.isEmpty()) qFatal("Cannot determine settings storage location");
QDir d{path};
QString filepath = returnCSVFilePath("dbPID");
if (d.mkpath(d.absolutePath()) && QDir::setCurrent(d.absolutePath()))
{
qDebug() << "settings in" << QDir::currentPath();
QFile f{filepath};
if (f.open(QIODevice::ReadWrite | QIODevice::Append))
{
QTextStream stream(&f);
stream << newUser.getUserFirstName() << ","
<< newUser.getUserIDNumber() << "\n";
}
}
}
Am now testing to make this work with my Read functions.
If there's any way to be able to write to the Qt program's folder (or build folder), I'd really appreciate it!
You can not write to a Qt resource. If you want to update/write to a file you should put the file into a writeable filesystem. To e.g. place it in the appdata folder you can use QStandardPaths::writeableLocation(QStandardPaths::ApplicationsLocation)

Minimal Example on how to read, write and print QJson code (with QJsonDocument, QJsonArray, QJsonObject, QFile)

I am searching for a minimal full executable qt or c++ code example to parse and write this Json code:
{
"FirstName": "John",
"LastName": "Doe",
"MiddleName": null,
"Age": 43,
"Address": {
"Street": "Downing Street 10",
"City": "London",
"Country": "Great Britain"
},
"Phone numbers": [
"+44 1234567",
"+44 2345678"
]
"Valid":true,
}
The above example consists of an object with 5 key/value pairs. Two of the values are strings, one is a number,
one is another object and the last one an array.
A valid JSON document is either an array or an object, so a document always starts with a square or curly bracket.
EDIT:
Json has 2 more key/value pairs - value 'null' and 'bool'
And yes, I have seen a "Save Game Example"
and tried to figure it out.
But after nearly a week I gave up to transfer a minimal Example without enums, QVectors and 3 different header Files over to my project to handle the code snippet. Doesn't matter if its for a widget or core code.
I already did a xml read and write program successfully but it seems I miss some Important point and get errors with json that may or may not have to do with the parsing. I am not able to rule it out without a minimal fully working code example.
So my question is: Could you please provide a minimal Example to write, read and print the Json file? Thanks upfront.
Since there does not seem to be a clear example on SO I wrote one.
It uses simple mutation to create the objects, but if you have access to C++11 you should take a look at the std::initializer_list constructors, they make construction of objects much more compact.
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QtGlobal>
#include <QTextStream>
#include <QDebug>
int main() {
// 1. Create the document
QJsonObject root;
root["FirstName"] = "John";
root["LastName"] = "Doe";
root["Age"] = 43;
// Construct nested object first, then store it in `root`
QJsonObject Address;
Address["Street"] = "Downing Street 10";
Address["City"] = "London";
Address["Country"] = "Great Britain";
root["Address"] = Address;
QJsonArray PhoneNumbers;
PhoneNumbers.push_back("+44 1234567");
PhoneNumbers.push_back("+44 2345678");
root["Phone Numbers"] = PhoneNumbers;
// `ba` contains JSON
QByteArray ba = QJsonDocument(root).toJson();
QTextStream ts(stdout);
ts << "rendered JSON" << endl;
ts << ba;
{
QFile fout("test.json");
fout.open(QIODevice::WriteOnly);
fout.write(ba);
}
// 2. Now read it back in
QJsonParseError parseError;
QJsonDocument doc2;
{
QFile fin("test.json");
fin.open(QIODevice::ReadOnly);
QByteArray ba2 = fin.readAll();
doc2 = QJsonDocument::fromJson(ba2, &parseError);
}
if (parseError.error != QJsonParseError::NoError) {
qWarning() << "Parse error at" << parseError.offset << ":" << parseError.errorString();
} else {
ts << "parsed JSON" << endl;
ts << doc2.toJson(QJsonDocument::Compact);
//or QJsonDocument::Indented for a JsonFormat
}
}
I guess it might help someone else too, I figured it all out,
thanks to #Botje for his example, really helped.
In general it is easy for me to remember to represent data as QByteArray
in the memory.
A minimal example to access and also use every single value stored in a json file, you are welcome:
(Yes, I could have written it even more compact but I guess this makes it easier to understand, so I spared functions/classes/structs for the sake of readability.)
#include <QCoreApplication>
#include <QString>
#include <QVariant>
#include <QFile>
#include <QByteArray>
#include <QTextStream>
#include <QDebug>
//json specific
#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>
void writeJsonFile();
void readJsonFile();
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
writeJsonFile();
readJsonFile();
return a.exec();
}
void writeJsonFile()
{
qDebug() << "Write Json File: ";
//1. Create a QJsonObject and add values to it
QJsonObject jsonObj;
jsonObj["FirstName"] = "John";
//no value will be written as null in the json file
jsonObj["MiddleName"];
jsonObj["LastName"] = "Doe";
jsonObj["Age"] = 43;
//2. Create Item of Json Object content (object of object)
QJsonObject jsonItemObj;
jsonItemObj["Street"] = "Downing Street 10";
jsonItemObj["City"] = "London";
jsonItemObj["Country"] = "Great Britain";
//3. Add jsonItemObj to jsonObj and give it an object Name
jsonObj["Address"] = jsonItemObj;
//4. Create jsonArray and fill it with values - similar to filling a vector
QJsonArray jsonArray;
jsonArray.append("+44 1234567");
jsonArray.append("+44 2345678");
//Add a bool to the Object
jsonObj["Valid"] = true;
//5. Add jsonArray to jsonObj and give it an object Name
jsonObj["Phone numbers"] = jsonArray;
//(It can also be added to the jsonItemObj to be inline with the Address section)
//with jsonItemObj["Phone numbers"] = jsonArray or as much objects of objects
//you need
/* As I understood it, most Qt classes use a QByteArray to handle data internally
* because it is really fast/efficient,
* also QFile QIODevice, it is a good idea to hold the read/write
* QIODevice data as QByteArray in the Memory
*/
//6. Create a QByteArray and fill it with QJsonDocument (json formatted)
QByteArray byteArray;
byteArray = QJsonDocument(jsonObj).toJson();
//7. Open a QFile and write the byteArray filled with json formatted data
//thanks to the QJsonDocument() Class to the file
QFile file;
file.setFileName("file.json");
if(!file.open(QIODevice::WriteOnly)){
qDebug() << "No write access for json file";
return;
}
//8. finally write the file and close it
file.write(byteArray);
file.close();
//9. Print out the byteArray to the terminal
QTextStream textStream(stdout);
textStream << "Rendered json byteArray text: " << endl;
textStream << byteArray << endl;
}
void readJsonFile()
{
qDebug() << "Read Json File:";
//1. Open the QFile and write it to a byteArray and close the file
QFile file;
file.setFileName("file.json");
if(!file.open(QIODevice::ReadOnly)){
qDebug() << "Json filef couldn't be opened/found";
return;
}
QByteArray byteArray;
byteArray = file.readAll();
file.close();
//2. Format the content of the byteArray as QJsonDocument
//and check on parse Errors
QJsonParseError parseError;
QJsonDocument jsonDoc;
jsonDoc = QJsonDocument::fromJson(byteArray, &parseError);
if(parseError.error != QJsonParseError::NoError){
qWarning() << "Parse error at " << parseError.offset << ":" << parseError.errorString();
return;
}
//3. Create a jsonObject and fill it with the byteArray content, formatted
//and holding by the jsonDocument Class
QJsonObject jsonObj;
jsonObj = jsonDoc.object();
//4. Now picking the jsonValues and printing them out or do what ever you need
QJsonValue jsonVal;
QTextStream textStream(stdout);
jsonVal = jsonObj.value("FirstName");
textStream << jsonVal.toString() << endl;
jsonVal = jsonObj.value("MiddleName");
//null gets back to an empty fild - added the sting "null/empty" to make it visible
textStream << jsonVal.toVariant().toString() << "null/empty" << endl;
jsonVal = jsonObj.value("LastName");
textStream << jsonVal.toString() << endl;
//The number has to be converted to an int and than to a string to print it
jsonVal = jsonObj.value("Age");
textStream << QString::number(jsonVal.toInt()) << endl;
//5. Now we need to fill the object of the object. To do that, we need
//the Item Object and a jsonSubVal object for json without a loop
QJsonObject jsonItemObj;
QJsonValue jsonSubVal;
jsonVal = jsonObj.value(QString("Address"));
jsonItemObj = jsonVal.toObject();
jsonSubVal = jsonItemObj["Street"];
textStream << jsonSubVal.toString() << endl;
jsonSubVal = jsonItemObj["City"];
textStream << jsonSubVal.toString() << endl;
jsonSubVal = jsonItemObj["Country"];
textStream << jsonSubVal.toString() << endl;
//6. now the Phone Numbers array with a loop
QJsonArray jsonArray;
jsonArray = jsonObj["Phone numbers"].toArray();
for(int i = 0; i < jsonArray.size(); i++)
textStream << jsonArray[i].toString() << endl;
textStream << "or with foreach" << endl;
foreach(QJsonValue v, jsonArray)
textStream << v.toString() << endl;
//7. And finally the bool value:
jsonVal = jsonObj.value(QString("Valid"));
textStream << jsonVal.toVariant().toString() << endl;
textStream << "or as number, not a string: ";
textStream << (QString::number(jsonVal.toInt())) << endl;
textStream << "\nThe whole file as printed in the file \n" <<
jsonDoc.toJson(QJsonDocument::Indented);

Qt Sqlite error 1 - table already exists

I have a problem with Qt and sqlite. Until recently, I didn't have any problems creating tables, but now, whenever I try to create a table (using the exact same function) I get an error message:
QSqlError("1", "Unable to fetch row", "table selections already exists")
My query strings are as follows:
CREATE TABLE external_files (path VARCHAR (255) NOT NULL, used INTEGER (12) NOT NULL);
This is the same as before too.
The strange thing is though, all the tables are created without a problem, but I still get error messages.
If you have any ideas why this happens, I would appreciate it. :)
UPDATE: Minimal, complete, verifiable example:
#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QFile>
#include <QTextStream>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "CONN");
db.setDatabaseName("test.db");
if(!db.open()){
qDebug() << "Connection failed!";
}
QFile tableListFile(":/resources/sql/tables.sql");
if(tableListFile.open(QIODevice::ReadOnly))
{
QTextStream stream(&tableListFile);
while(!stream.atEnd())
{
QString queryString = stream.readLine();
qDebug() << "Query string: " << queryString;
QSqlQuery query(queryString, db);
if(!query.exec()){
qDebug() << "Query error: " << query.lastError();
}
}
}
db.close();
return a.exec();
}
Thanks in advance.
Ok, I managed to solve the problem.
The query was executed 2 times for each query string, and that caused the error messages.
To get to the solution, i edited my code like this:
QString queryString = stream.readLine();
qDebug() << "Query string: " << queryString;
QSqlQuery query;
if(!query.exec(queryString)){
qDebug() << "Query error: " << query.lastError();
}

Qt - Writing integer data into JSON

I am using Qt (5.5) and I want to exchange data in JSON format in a client-server application.
So the format is constant:
{
"ball":
{
"posx": 12,
"posy": 35
}
}
I would like to be able to define a ByteArray or string like so:
QByteArray data = "{\"ball\":{\"posx\":%s,\"posy\":%s}}"
and then just write whatever the values for that into the string.
How do I do that?
QtJson is baked into Qt 5. It is easy to use, and gets it all ready for you pretty easily.
#include <QCoreApplication>
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
void saveToJson(QJsonObject & json);
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QJsonObject jsonObject;
saveToJson(jsonObject);
QJsonDocument jsonDoc(jsonObject);
qDebug() << "Example of QJsonDocument::toJson() >>>";
qDebug() << jsonDoc.toJson();
qDebug() << "<<<";
return a.exec();
}
void saveToJson(QJsonObject & json)
{
QJsonObject ball;
ball["posx"] = 12;
ball["posy"] = 35;
json["ball"] = ball;
}
output
Example of QJsonDocument::toJson() >>>
"{
"ball": {
"posx": 12,
"posy": 35
}
}
"
<<<
Note: qDebug() wraps QString objects in quotes when printing. To get rid of that, pass your QString into qPrintable(). And it puts endl in for you at the end of each line.
For a more complex example see the official:
JSON Save Game Example
http://doc.qt.io/qt-5/qtcore-json-savegame-example.html
Hope that helps.
And here are more examples of string manipulations, but for readability and maintainability, please use the QJson classes.
QString str;
str = QString("{\"ball\":{\"posx\":%1,\"posy\":%2}}").arg(12).arg(35);
qDebug() << qPrintable(str);
QByteArray ba = str.toLocal8Bit();
qDebug() << ba;
QString str2;
str2 = "{\"ball\":{\"posx\":"
+ QString::number(12)
+ ",\"posy\":"
+ QString::number(35)
+ "}}";
qDebug() << qPrintable(str2);
output
{"ball":{"posx":12,"posy":35}}
"{"ball":{"posx":12,"posy":35}}"
{"ball":{"posx":12,"posy":35}}
Note again that the quotes are added by qDebug() when printing a QByteArray object.
Hope that helps.

Qt parse a json response

I need to parse a json response that looks like this and to get the id value, i.e. blabla2:
{
"kind": "blabla",
"id": "blabla2",
"longUrl": "blabla3"
}
How do I do that? I tried to use Qjson, but when I try to buid it to get the .dll, I get an error:
xlocale.h is missing.
Are there other alternatives? Thanks.
Looking at the documentation for QJsonDocument you can read the file into a QByteArray and then do the following: -
// assuming a QByteArray contains the json file data
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(byteArray, &err);
// test for error...
Then use the function on the QJsonDocument to retrieve the top level object...
if(doc.isObject())
{
QJsonObject obj = doc.object();
QJsonObject::iterator itr = obj.find("id");
if(itr == obj.end())
{
// object not found.
}
// do something with the found object...
}
There is also a value() function in QJsonObject, so instead of using the iterator and calling find, you may simply be able to call: -
QJsonValue val = obj.value("id");
Disclaimer: I present this code after having just read the Qt documentation, so do not just copy and paste this, but consider it more as pseudo code. You may need to edit it a little, but hope that helps.
I would encourage you to use Qt 5 or backport the json classes to Qt 4. Your software will be more future proof then when you intend to port it to Qt 5 since you will need to rewrite the json parsing then available in QtCore.
I would write something like the code below, but please double check it before using it in production as I may have missed an error checking while writing it. Regardless of error checking, the output is what you wanted to get.
main.cpp
#include <QFile>
#include <QByteArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
int main()
{
QFile file("main.json");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Could not open the file" << file.fileName() << "for reading:" << file.errorString();
return 1;
}
QByteArray jsonData = file.readAll();
if (file.error() != QFile::NoError) {
qDebug() << QString("Failed to read from file %1, error: %2").arg(file.fileName()).arg(file.errorString());
return 2;
}
if (jsonData.isEmpty()) {
qDebug() << "No data was currently available for reading from file" << file.fileName();
return 3;
}
QJsonDocument document = QJsonDocument::fromJson(jsonData);
if (!document.isObject()) {
qDebug() << "Document is not an object";
return 4;
}
QJsonObject object = document.object();
QJsonValue jsonValue = object.value("id");
if (jsonValue.isUndefined()) {
qDebug() << "Key id does not exist";
return 5;
}
if (!jsonValue.isString()) {
qDebug() << "Value not string";
return 6;
}
qDebug() << jsonValue.toString();
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
"blabla2"