Qt save strings to JSON - c++

Im trying to save my waypoints into .json file, but it is always saving the wrong string.. which I dont know where it is coming.
This is my code:
for (int i = 1; i < path->GetPointCount(); i++) {
Vector lonlatalt;
path->GetPointLocation(i).GetGeodetic(&lonlatalt);
sstr << "waypoint";
sstr << i;
sstr << " ";
sstr << lonlatalt.x;
sstr << " ";
sstr << lonlatalt.y;
sstr << " 0.0";
sstr << std::endl;
std::string alt = " 0.0";
QJsonObject json;
json["waypoint"+i] = lonlatalt.x, lonlatalt.y, alt;
QJsonDocument json_doc(json);
QString json_string = json_doc.toJson();
QString filename = "./tempwpf";
QFile save_file(filename);
if(!save_file.open(QIODevice::WriteOnly)){
qDebug() << "failed to open save file";
}
save_file.write(json_string.toLocal8Bit());
save_file.close();
}
The output of json file should be:
{
"waypoint0": "10.05456, 51.02453, 0.0",
"waypoint1": "10.05456, 51.02453, 0.0",
"waypoint2": "10.05456, 51.02453, 0.0",
"waypoint3": "10.05456, 51.02453, 0.0",
"waypoint4": "10.05456, 51.02453, 0.0",
"waypoint5": "10.05456, 51.02453, 0.0",
"waypoint6": "10.05456, 51.02453, 0.0"
}

json["waypoint"+i] = lonlatalt.x, lonlatalt.y, alt;
There's not a single right thing in this line.
"waypoint"+i
"waypoint" is a string literal, i.e. an array of char, which decays to a pointer to its first element; now, if you sum an integer to a pointer, you get an incremented pointer, i.e. a pointer that points i elements after the first.
Hence, here you are effectively taking a substring of "waypoint" starting at the i-th character. "waypoint"+1 => "aypoint"; "waypoint"+2 => "ypoint" and so on.
Given that you are using Qt and QString anyhow to put this into the JSON document, you can simply use the Qt string formatting methods; so, on the left hand side you can do:
json[QString("waypoint%1").arg(i)]
On the right hand side, it's all wrong again; here you are mistakingly using the comma operator, which evaluates the expressions at both its sides and discards the left one; so when you write
lonlatalt.x, lonlatalt.y, alt
you are actually writing
(lonlatalt.x, lonlatalt.y), alt
which boils down to plain alt.
Now, if you want to format this as a comma-separated string, you can again use the QString methods:
json[QString("waypoint%1").arg(i)] = QString("%1, %2, 0.0").arg(lonlatalt.x, 0, 'f', 5).arg(lonlatalt.y, 0, 'f', 5)
Finally, you are creating/saving a new QJsonDocument at each iteration, so every time you create a file with just one waypoint and overwrite the previous one. You should move the creation/save outside of the loop:
QJsonObject json;
goes before the for
json["waypoint"+i] = lonlatalt.x, lonlatalt.y, alt;
remains inside, and all the rest outside. Notice that even the code to save is suboptimal: json_doc.toJson() returns a QByteArray already in UTF-8 (which is generally used as the "canonical" encoding for JSON); you are reconverting it to a QString and then converting it back to an 8 bit encoding (which is lossy and most probably unintended).
Also, you check if the file was opened successfully, and write on it even in case of error - that's definitely a bad idea.
Finally, close() is not needed - it's performed automatically when the QFile goes out of scope.
To sum it up:
QFile save_file("./tempwpf");
if(save_file.open(QIODevice::WriteOnly)){
QJsonDocument json_doc(json);
save_file.write(json_string.toJson());
} else {
qDebug() << "failed to open save file";
}

Related

QString::replace doesnt replace

Problem
I am trying to convert a string to a C string. In doing so, I need to replace " with \". I use the following code to do this:
QString Converter::plain2C(const QString &in) {
QString out;
// Split input in each line
QStringList list = in.split(QChar('\n'));
for (int i = 0; list.length() > i; i++) { // Go throught each line of the input
QString line = list[i]; // Thats the line
line.replace(QChar('\\'), QLatin1String("\\\\")); // replace \ with \\
line.replace(QChar('"'), QLatin1String("\\\"")); // replace " with \"
// For printf()
if(escapePercent)
line.replace(QChar('%'), QLatin1String("%%"));
// If option "Split output into multiple lines" is active add a " to the output
if (multiLine)
out.append(QChar('"'));
// append the line to the output
out.append(line);
// append a "\n" to the output because we are at the end of a line
if (list.length() -1 > i)
out.append(QLatin1String("\\n"));
// If option "Split output into multiple lines" is active add a " and \n to the output
if (multiLine) {
out.append(QChar('"'));
out.append(QChar('\n'));
}
}
if (!multiLine) {
out.prepend(QChar('"'));
out.append(QChar('"'));
}
return out;
}
However, " is still there without a \ before.
Information
Qt Version 5.15.3
C++17
Edit
The application is used to enter a normal string copied from the Internet and get as a result a string that can be copied into a C/C++ program.
More code
void Converter::run()
{
if (_from != From::NotSupportet &&
_to != To::toInvalid) {
QString out;
// More code obove and below
else if (_from == From::Plain) {
switch (_to) {
case To::toCString:
out = plain2C(_in);
break;
// Emits the ready string which is applied direct to a QPlainTextEdit
emit htmlReady(out);
}
}
Edit 2
Added more comments to the code
It works now. The problem was the line above:
line.replace(QChar('\'), QLatin1String("\\\\")); // replace \ with \\
The problem was that the comment ended with 2 \. That somehow disabled the next line or something like that.
Anyway, this is the working code:
QString Converter::plain2C(const QString &in) {
QString out;
// Split input in each line
QStringList list = in.split(QChar('\n'));
for (int i = 0; list.length() > i; i++) { // Go throught each line of the input
QString line = list[i]; // Thats the line
line.replace(QChar('\\'), QLatin1String("\\\\")); // replace "\" with "\\"
line.replace(QChar('"'), QLatin1String("\\\"")); // replace " with \"
// For printf()
if(escapePercent)
line.replace(QChar('%'), QLatin1String("%%"));
// If option "Split output into multiple lines" is active add a " to the output
if (multiLine)
out.append(QChar('"'));
// append the line to the output
out.append(line);
// append a "\n" to the output because we are at the end of a line
if (list.length() -1 > i)
out.append(QLatin1String("\\n"));
// If option "Split output into multiple lines" is active add a " and \n to the output
if (multiLine) {
out.append(QChar('"'));
out.append(QChar('\n'));
}
}
if (!multiLine) {
out.prepend(QChar('"'));
out.append(QChar('"'));
}
return out;
}

QDir mkpath returns true, but directory not created

I'm having a weird issue with creating paths using Qt on Linux. I've written a standalone test program that creates paths and tests for their existence. This works as expected and creates the directory.
/* make path */
QString p("/usr2/archive/S1234ABC/5/6");
QDir d;
if (d.mkpath(p)) qDebug() << "mkpath() returned true";
else qDebug() << "mkpath() returned false";
QDir d2;
if (d2.exists(p)) qDebug() << "exists() returned true";
else qDebug() << "exists() returned false";
I made that test example into a more robust function, in another project. But it isn't working... mkpath() and exists() return true, but the paths don't exist on the hard disk.
bool nidb::MakePath(QString p, QString &msg) {
if ((p == "") || (p == ".") || (p == "..") || (p == "/") || (p.contains("//")) || (p == "/root") || (p == "/home")) {
msg = "Path is not valid [" + p + "]";
return false;
}
WriteLog("MakePath() called with path ["+p+"]");
QDir path;
if (path.mkpath(p)) {
WriteLog("MakePath() mkpath returned true [" + p + "]");
if (path.exists()) {
WriteLog("MakePath() Path exists [" + p + "]");
msg = QString("Destination path [" + p + "] created");
}
else {
WriteLog("MakePath() Path does not exist [" + p + "]");
msg = QString("Unable to create destination path [" + p + "]");
return false;
}
}
else {
msg = QString("MakePath() mkpath returned false [" + p + "]");
return false;
}
return true;
}
The output from my program:
[2019/06/04 13:19:37][26034] MakePath() called with path [/usr2/archive/S0836VYL/6/5/dicom]
[2019/06/04 13:19:37][26034] MakePath() mkpath returned true [/usr2/archive/S0836VYL/6/5/dicom]
[2019/06/04 13:19:37][26034] MakePath() Path exists [/usr2/archive/S0836VYL/6/5/dicom]
and the output from the command line...
[onrc#ado2dev /]$ cd /usr2/archive/S0836VYL/6/5/dicom
-bash: cd: /usr2/archive/S0836VYL/6/5/dicom: No such file or directory
[onrc#ado2dev /]$
What am I missing??
Try to use this :
QString p("/usr2/archive/S1234ABC/5/6");
QDir d(p);
if(!d.exists() && !d.mkpath(p)) qDebug() << "Error: can't create folder '"<< p <<"'.";
else qDebug() << "Folder '"<< p <<"' exists or created successfully".
Hope it helps you.
All right, this is one for the record books...
The issue was a null terminator appended to the end of the S1234ABC string before being inserted into the database. That string was later used to create the above paths. That S1234ABC string was created using the following code:
QString prefix = "S";
QChar C1, C2, C3, etc... (randomly generated characters)
QString newID = prefix + C1 + C2 + etc...
This created a QString with a \0 on the end of it. Qt stored this value in the MySQL database, which I then pulled back into Qt and tried to make a path with it. Since it's a null terminated string, it appears normal on phpMyAdmin, in xterm, and the log files. Except... in puTTY in Windows, where I saw the weird path it was trying to create:
/usr2/archive/S0836VYL\u0000/10/3/dicom
Thankfully puTTY displayed the actual unicode value instead of ignoring it. Thank you putty! I never, ever would've figured that out...
Recreating the S1234ABC string using QStrings for each character instead of QChar fixed the issue. Now I have regular old strings in the database, and normal paths.

Counting the number of entries in JSON and executing the code accordingly

I have an Json file which is include waypoints. The file can be have more or fewer recordings.
{
"waypoint0": "10.05456, 51.02453, 0.0",
"waypoint1": "10.05456, 51.02453, 0.0",
"waypoint2": "10.05456, 51.02453, 0.0",
"waypoint3": "10.05456, 51.02453, 0.0",
"waypoint4": "10.05456, 51.02453, 0.0",
"waypoint5": "10.05456, 51.02453, 0.0",
"waypoint6": "10.05456, 51.02453, 0.0",
}
First I want to read the JSON file:
std::vector route;
QString filename = QFileDialog::getOpenFileName(this,
tr("Flugweg laden"), "",
tr("Wegpunkt Datei (*.wpf)"));
QString val;
QFile file(filename);
file.open(QIODevice::ReadOnly | QIODevice::Text);
val = file.readAll();
file.close();
QJsonDocument d = QJsonDocument::fromJson(val.toUtf8());
QJsonObject sett2 = d.object();
QJsonObject it = d.object();
for(auto it = sett2.begin(); it != sett2.end(); ++it) {
//TODO
}
Now I want to execute code for each object with changes where //TODO is written.
est::aladin::Waypoint waypoint0; // waypoint0 should be changed on the name of the values like in my JSON File. waypoint0 to waypoint6
waypoint0.id = 0; // waypoint0 меняется в соответствии кол-во запись на waypoint1 и далее.
waypoint0.displayed_number = -1; // the first waypoint should be have the number -1 and the last -2, waypoints beetween from 1 and till 98.
waypoint0.latitude_wgs84_radians = lat; //first value from waypoint0 - in this example - 10.05456 and etc.
waypoint0.longitude_wgs84_radians = long; //second value from waypoint0 - in this example - 51.02453 and etc.
waypoint0.altitude_wgs84_meters = alt; //third value from waypoint0 - in this example - 0.0 and etc.
waypoint0.type = est::aladin::WaypointType::GroundControlStation; // if it is waypoint0
//between the first and last waypoint WAYPOINT+NUMBER.type = est::aladin::WaypointType::FlyOver;
//Last waypoint WAYPOINT+NUMBER.type = est::aladin::WaypointType::Landing;
route.push_back(waypoint0); // waypoint0 should be changed on which waypoint we will push now.
How to parse JSON line "waypoint0": "10.05456, 51.02453, 0.0", and output to different variables.
Your .json is not adequate, I think it is a mistake to copy and paste it (there is an unnecessary comma in the last line).
The first thing we must do is obtain the QJsonObject, then iterate through its keys(), the result is string in csv format, so we must separate it with split, then we convert each item to double with toDouble().
QString filename = QFileDialog::getOpenFileName(this,
tr("Flugweg laden"), "",
tr("Wegpunkt Datei (*.wpf)"));
if(!filename.isEmpty()){
QByteArray val;
QFile file(filename);
file.open(QIODevice::ReadOnly | QIODevice::Text);
val = file.readAll();
file.close();
QJsonDocument d = QJsonDocument::fromJson(val);
QJsonObject obj = d.object();
for(const QString &key: obj.keys() ){
QString line = obj[key].toString();
QStringList elements = line.split(",");
double lat= elements[0].toDouble();
double lng= elements[1].toDouble();
double alt= elements[2].toDouble();
qDebug()<<key<< lat<<lng<<alt;
est::aladin::Waypoint waypoint;
[...]
waypoint.latitude_wgs84_radians = lat;
waypoint.longitude_wgs84_radians = lng;
waypoint.altitude_wgs84_meters = alt;
[...]
route.push_back(waypoint);
}
}
Note: Do not use long as the name of a variable since it is a reserved name.

Writing a file larger than 2Gb

I write a file storing all the raw data I read from two external sensors and I realized that program stops writing data when the file gets 2Gb, the program continuous working but it does not write anything more in the file.
I am using the next code (it is one threads that waits the signal and writes in the file):
while(1)
{
//Lock, Wait the signal and unlock
pthread_mutex_lock(&mutex_WriteOFIMUandGPS);
pthread_cond_wait(&cond_WriteOFIMUandGPS, &mutex_WriteOFIMUandGPS);
X_CopySnapshot = X_Snapshot;
Y_CopySnapshot = Y_Snapshot;
Z_CopySnapshot = Z_Snapshot;
Vx_CopySnapshot = Vx_Snapshot;
Vy_CopySnapshot = Vy_Snapshot;
Vz_CopySnapshot = Vz_Snapshot;
HPL_CopySnapshot = HPL_Snapshot;
HDOP_CopySnapshot = HDOP_Snapshot;
StdPosX_CopySnapshot = StdPosX_Snapshot;
StdPosY_CopySnapshot = StdPosY_Snapshot;
StdPosZ_CopySnapshot = StdPosZ_Snapshot;
pthread_mutex_unlock(&mutex_WriteOFIMUandGPS);
//Get Time and Date
now = time(0);
localtm = localtime(&now);
//Get millis
gettimeofday(&tp, NULL);
ms = round(tp.tv_usec / 1000);
sprintf(buf,"%03d",(int)ms);
//Empty strings
DateTime = "";
text.clear();
text.str(string());
//Store Data and Time information in a string
text << localtm->tm_year+1900 << "/";
text << (((localtm->tm_mon+1)<10) ? "0" : "") << localtm->tm_mon+1 << "/";
text << ((localtm->tm_mday<10)? "0" : "") << localtm->tm_mday << ",";
text << ((localtm->tm_hour<10)? "0" : "") << localtm->tm_hour << ":";
text << ((localtm->tm_min<10)? "0" : "") << localtm->tm_min << ":";
text << ((localtm->tm_sec<10)? "0" : "") << localtm->tm_sec << "."<< buf;
DateTime = text.str();
//Save data
fprintf(fid,"%s,"
"2,6,0," // Alg_ID,SolStatus,EGNOSStatus,
"%12.3f,%12.3f,%12.3f," // XyzUKF[0],XyzUKF[1],XyzUKF[2],
"%8.8f,%8.8f,%8.8f,%7.18f," // V_UKF[0],V_UKF[1],V_UKF[2],HPL,
"0,0,%5.2f,nan,nan,nan,nan," // NumSat,NumSatEx,HDOP,PDC1,PDC2,PDC3,PDC4,
"%7.2f,%7.2f,%7.2f," // StdPos[0],StdPos[1],StdPos[2]
"0,0,0," // StdVel[0],StdVel[1],StdVel[2],
"1,0," // TypePositioning[0],TechUsedPos,
"0,0,0,0,0," // Observables
"0,0,0,0,0," // Observables
"0,0," // VPL,VDOP
"0," // TechRec
"0,0,0,0,0," // Observables
"0,0,0,0,0\n", // Observables
DateTime.c_str(),
X_CopySnapshot, Y_CopySnapshot, Z_CopySnapshot,
Vx_CopySnapshot, Vy_CopySnapshot, Vz_CopySnapshot,HPL_CopySnapshot,
HDOP_CopySnapshot,
StdPosX_CopySnapshot, StdPosY_CopySnapshot, StdPosZ_CopySnapshot);
}
I do not know if there is any way to write files larger than 2Gb and how I should achieve it.
Many thanks for your help.
As you state that you use linux: man 2 open:
O_LARGEFILE
(LFS) Allow files whose sizes cannot be represented in an off_t (but can be represented
in an off64_t) to be opened. The _LARGEFILE64_SOURCE macro must be defined (before
including any header files) in order to obtain this definition. Setting the _FILE_OFF-
SET_BITS feature test macro to 64 (rather than using O_LARGEFILE) is the preferred
method of accessing large files on 32-bit systems (see feature_test_macros(7)).
Otherwise, file operations are limited to 2GiB.

QSettings: How to read array from INI file

I wanna read comma separated data form INI file. I've already read here:
QSettings::IniFormat values with "," returned as QStringList
How to read a value using QSetting if the value contains comma character
...that commas are treated as separators and QSettings value function will return QStringList.
However, my data in INI file looks like this:
norm-factor=<<eof
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
eof
I don't need a whole matrix. All rows joined up together are fair enough for me. But can QSettings handle such structure?
Should I read this using:
QStringList norms = ini->value("norm-factor", QStringList()).toStringList();
Or do I have to parse it in another way?
The line breaks are a problem since INI files use line breaks for their own syntax.
Qt seems to not support your type of line continuation (<<eol ... eol).
QSettings s("./inifile", QSettings::IniFormat);
qDebug() << s.value("norm-factor");
yields
QVariant(QString, "<<eof")
The <<eol expression might be invalid INI in itself. (Wikipedia on INI files)
I suggest you parse the file manually.
Ronny Brendel's answer is correct ...i am only adding code that solves above problem ...it creates temporary INI file with corrected arrays:
/**
* #param src source INI file
* #param dst destination (fixed) INI file
*/
void fixINI(const QString &src, const QString &dst) const {
// Opens source and destination files
QFile fsrc(src);
QFile fdst(dst);
if (!fsrc.open(QIODevice::ReadOnly)) {
QString msg("Cannot open '" + src + "'' file.");
throw new Exception(NULL, msg, this, __FUNCTION__, __LINE__);
}
if (!fdst.open(QIODevice::WriteOnly)) {
QString msg("Cannot open '" + dst + "'' file.");
throw new Exception(NULL, msg, this, __FUNCTION__, __LINE__);
}
// Stream
QTextStream in(&fsrc);
QTextStream out(&fdst);
bool arrayMode = false;
QString cache;
while (!in.atEnd()) {
// Read current line
QString line = in.readLine();
// Enables array mode
// NOTE: Clear cache and store 'key=' to it, without '<<eof' text
if (arrayMode == false && line.contains("<<eof")) {
arrayMode = true;
cache = line.remove("<<eof").trimmed();
continue;
}
// Disables array mode
// NOTE: Flush cache into output and remove last ',' separator
if (arrayMode == true && line.trimmed().compare("eof") == 0) {
arrayMode = false;
out << cache.left(cache.length() - 1) << "\n";
continue;
}
// Store line into cache or copy it to output
if (arrayMode) {
cache += line.trimmed() + ",";
} else {
out << line << "\n";
}
}
fsrc.close();
fdst.close();
}