c++ qt can i fill a QVariant with multiple struct objects - c++

I have the following struct:
struct FileInfo {
QString fileName;
QString fileSize;
QString md5Sum;
};
is it possible to put approximately 30 such objects into a QVariant and then be able to iterate through the QVariant to retrieve one of the structs based on index and then cast the object back into a struct and query the struct for say fileSize of a specific file? Is there a better approach to my issue?

You can consider QVariantList to store object as QVariant. Of course, you can convert back into your custom struct.
Example.
struct FileInfo {
QString fileName;
QString fileSize;
QString md5sum;
};
int main()
{
QVariantList variantList;
for(int i = 0; i < 30; i++) {
FileInfo info{QString("File %1").arg(i), QString("size %1").arg(i), QString("md5sum %1").arg(i)};
QVariant var;
var.setValue(info);
variantList.append(var);
}
for (auto v: variantList) {
FileInfo f = v.value<FileInfo>();
qDebug() << "File name: " << f.fileName << "\tfile size: " << f.fileSize;
}
return 0;
}

Related

How to extract value from json array with QJsonDocument format

I'm getting a json format like this and I want to get the value of "Duration", "Id", "LoadCumulLimit" and "Notes".
QJsonDocument({"d":{"results":[{"Duration":"420.000","Id":"123456789XYZ","LoadCumulLimit":"15.000","NavWpNioshToOpNoish":{"__deferred":{"uri":"http://xxx/WorkplaceNOISHDataSet('123456789XYZ')/NavWpNioshToOpNoish"}},"Notes":"123456789XYZ","__metadata":{"id":"xxx/WorkplaceNOISHDataSet('123456789XYZ')","type":"xxx.WorkplaceNOISHData","uri":"xxx/WorkplaceNOISHDataSet('123456789XYZ')"}}]}})
I tried to do this but it doesn't work and it return empty with array
`
QJsonDocument document = QJsonDocument::fromJson(content.toUtf8());
QJsonArray documentArray = document.array();
QStringList wordList;
for (const QJsonValue &i : documentArray)
{
//qInfo() << i.toString() << endl;
wordList << i.toString();
}
Could you guys give me a help or any suggest?
You could convert the QJsonDocument to a QVariant. Then you can use QVariantMap or QVariantList to walk the document and use the appropriate toString() or toDouble() to retrieve the values.
The following is hard-coded to your JSON there are only minimal validation checks included. (i.e. it is a disclaimer that the code is presented for educational purposes only and made not be production ready).
bool parse()
{
QString json = "{\"d\":{\"results\":[{\"Duration\":\"420.000\",\"Id\":\"123456789XYZ\",\"LoadCumulLimit\":\"15.000\",\"NavWpNioshToOpNoish\":{\"__deferred\":{\"uri\":\"http://xxx/WorkplaceNOISHDataSet('123456789XYZ')/NavWpNioshToOpNoish\"}},\"Notes\":\"123456789XYZ\",\"__metadata\":{\"id\":\"xxx/WorkplaceNOISHDataSet('123456789XYZ')\",\"type\":\"xxx.WorkplaceNOISHData\",\"uri\":\"xxx/WorkplaceNOISHDataSet('123456789XYZ')\"}}]}}";
QJsonDocument document = QJsonDocument::fromJson(json.toUtf8());
if (document.isEmpty() || document.isNull()) return false;
QVariantMap root = document.toVariant().toMap();
if (root.isEmpty()) return false;
QVariantMap d = root["d"].toMap();
if (d.isEmpty()) return false;
QVariantList results = d["results"].toList();
if (results.isEmpty()) return false;
foreach (QVariant varResult, results)
{
QVariantMap result = varResult.toMap();
if (result.isEmpty()) return false;
bool ok = true;
double duration = result["Duration"].toDouble(&ok);
if (!ok) return false;
QString id = result["Id"].toString();
if (id.isEmpty() || id.isNull()) return false;
double loadCumulLimit = result["LoadCumulLimit"].toDouble(&ok);
if (!ok) return false;
QString notes = result["Notes"].toString();
if (!notes.isEmpty() || notes.isNull()) return false;
qDebug() << id << duration << loadCumulLimit << notes; // "123456789XYZ" 420 15 "123456789XYZ"
}
return true;
}
Alternatively, you can just use QJsonDocument, QJsonValue and QJsonArray to walk the document and use the corresponding toString() and toDouble() to retrieve the values. Again, there are minimal validation checks included:
bool parse2()
{
QString json = "{\"d\":{\"results\":[{\"Duration\":\"420.000\",\"Id\":\"123456789XYZ\",\"LoadCumulLimit\":\"15.000\",\"NavWpNioshToOpNoish\":{\"__deferred\":{\"uri\":\"http://xxx/WorkplaceNOISHDataSet('123456789XYZ')/NavWpNioshToOpNoish\"}},\"Notes\":\"123456789XYZ\",\"__metadata\":{\"id\":\"xxx/WorkplaceNOISHDataSet('123456789XYZ')\",\"type\":\"xxx.WorkplaceNOISHData\",\"uri\":\"xxx/WorkplaceNOISHDataSet('123456789XYZ')\"}}]}}";
QJsonDocument document = QJsonDocument::fromJson(json.toUtf8());
if (document.isEmpty() || document.isNull()) return false;
QJsonValue d = document["d"];
if (d.isNull() || d.isUndefined()) return false;
QJsonArray results = d["results"].toArray();
if (results.isEmpty()) return false;
foreach (QJsonValue result, results)
{
double duration = result["Duration"].toDouble();
QString id = result["Id"].toString();
if (id.isEmpty() || id.isNull()) return false;
double loadCumulLimit = result["LoadCumulLimit"].toDouble();
QString notes = result["Notes"].toString();
if (!notes.isEmpty() || notes.isNull()) return false;
qDebug() << id << duration << loadCumulLimit << notes; // "123456789XYZ" 420 15 "123456789XYZ"
}
return true;
}
You have:
object d {
object results {
[ { several objects to be extracted} ]
}
}
To extract a value of an object by given a key, call operator[](key) on QJsonValue.
When you have an array, to extract its first item call operator[](0) on this array. When you have found an object at desired key, you can convert its value to the value of specified type by toString/toInt/toDouble etc. methods of QJsonValue.
Short version:
QJsonValue item0 = document["d"]["results"].toArray()[0];
QStringList wordList;
wordList << item0["Duration"].toString() << item0["Id"].toString() << item0["LoadCumulLimit"].toString() << item0["Notes"].toString();
the longer version:
QJsonValue dObj = document["d"];
QJsonValue resultsObj = dObj["results"];
QJsonArray resultsArray = resultsObj.toArray();
QJsonValue item0 = resultsArray[0];
QStringList wordList;
wordList << item0["Duration"].toString() << item0["Id"].toString() << item0["LoadCumulLimit"].toString() << item0["Notes"].toString();

QTableWidget to multiple files

I’ve got QTableWidget with data like this:
table.png
The table can contains only names from the QList:
QList<QString> shapes { "Triangle", "Circle", "Trapeze", "Square", "Rectangle", "Diamond" };
with random int values in the neighboring cell.
Table can contain all "shapes" or only a part of it (like in the example).
I try to create separate file for each shape form the table and write down corresponding int values to them.
To achieve this I wrote something like that:
QList<QTableWidgetItem *> ItemList
/.../
for(int i = 0; i < rows; ++i)
{
for(int i = 0; i<columns; ++i)
{
foreach(QString itm, shapes )
{
ItemList = ui->tableWidget->findItems(itm, Qt::MatchExactly);
QFile mFile(itm + ".txt");
if(mFile.open(QFile::ReadWrite))
{
for(int i = 0; i < ItemList.count(); ++i)
{
int rowNR = ItemList.at(i)->row();
int columnNR = ItemList.at(i)->column();
out << "Values = " << ui->tableWidget->item(rowNR, columnNR+1)->text() << endl;
}
}
}
mFile.flush();
mFile.close();
}
}
Files are created for every item from the QList – if the shape from the QList is not in the table, an empty file is created.
How to create files only on the basis of available names in the table?
You can write like this.
QList<QTableWidgetItem *> ItemList
/.../
for(QString str : Shapes){
ItemList = ui->tableWidget->findItems(itm, Qt::MatchExactly); // Get the matching list
if(ItemList.isEmpty(){
continue; // If shape does not exist in table skip the iteration
}
QFile mFile(str + ".txt");
if(!mFile.open(QFile::ReadWrite){
return; // This should not happen ; this is error
}
for(QTableWidgetItem *item : ItemList){
int row = item->row();
int col = item->column()+1; // since it is neighboring cell
QString Value = ui->tableWidget->item(row,col)->text();
mFile.write(Value.toUtf8()); // You can change the way in which values are written
}
mFile.flush();
mFile.close();
}

no match for 'operator[]' (operand types are 'QVariant' and 'const char [2]') QVariant/QVariantMap

Im trying to make the start of a wrapper class for json in qt 5.1 and i'm working on a function which which will check if the var inputted is a QVariantMap or just a QVariant and everything works well till i go the second level of the muli dimen array. here is my array structure and class code.
JsonHelper jh;
QVariantMap obj = jh.getJsonObjectVarientMap(data);
This causes me the problems, when i just use "obj" or "obj["1"]" there is no issues, only when i
// obj["4"]["3"] this causes the problems
qDebug() << "Your returned val is : " << jh.keySearchVal(obj["4"]["3"],arr_index_txt);
QMap<QString,QVariant> mp = obj["4"].toMap();
foreach(QString key,mp.keys())
{
// this works ok
qDebug() << "key : " << key << " : val : " << mp[key];
}
QVariantMap JsonHelper::getJsonObjectVarientMap(QString in)
{
QJsonDocument d = QJsonDocument::fromJson(in.toUtf8());
return d.object().toVariantMap();
}
QVariant JsonHelper::keySearchVal(QVariant source, QString key)
{
QString type(source.typeName());
if(type=="QVariantMap")
{
QMap<QString, QVariant> map = source.toMap();
foreach(QString key_inner,map.keys())
{
QVariant in = map[key_inner];
if(key_inner==key)
{
return getVariantVal(in);
}
}
}
return "";
}
QVariant JsonHelper::keySearchVal(QVariantMap source, QString key)
{
foreach(QString key_inner,source.keys())
{
if(key_inner==key)
{
return source[key_inner];
}
}
return "";
}
QVariant JsonHelper::getVariantVal(QVariant in)
{
qDebug() << "from variant";
QString type(in.typeName());
if(type=="QVariantMap")
{
return in.toMap();
}
return in;
}
// obj["4"]["3"] this causes the problems
That is invalid because QVariant does not have an operator[] overload. That is also what the compiler is trying to tell you with this:
no match for 'operator[]' (operand types are 'QVariant' and 'const char [2]') QVariant/QVariantMap
You will need to convert any nested QVariant explicitly to QVariantMap if that is the underlying data type. See the following method for details:
QMap QVariant::toMap() const
Returns the variant as a QMap if the variant has type() QMetaType::QVariantMap; otherwise returns an empty map.
It is not the main point, but you also have two further issues:
You seem to use the word Varient as opposed to Variant.
Your code lacks error checking and reporting for conversions, etc.

C++ Structure with unknown data types

My program reads user's input and create easy "table". User on the start specifies data types of columns and the number of rows.
User's input:
create table
add attribute string Name
add attribute int Age
rows 3
I need prepare a structure from user's input now. I have something like this:
CTable
{
unsigned attributesCnt;
string * attributesNames;
void ** attributes;
};
So, from the user's input, the progam does these steps:
CTable myTable;
myTable.attributesCnt = 2; // string "Name", int "Age"
myTable.attributesNames = new string[2];
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
attributes = new void[2]; // 2 attributes
attributes[0] = (void*) new string[3]; // there will be 3 rows
attributes[1] = (void*) new int[3];
I need remember that "attributes[0]" is string and "attributes[1]" is int too.
Is this "right" way?
I would like use only standard libraries.
What you are looking for is a tagged union also called a variant. It allows you to store multiple data types at the same location just like a regular union but includes an additional but separate data member that indicates it's type. The C++ Standard Library does not include variants but they are easy enough to implement.
Once you have a variant you can apply it to your example like below.
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
// I recommend using std::vector here instead of using new/delete yourself
attributes = new Variant*[2]; // 2 attributes
attributes[0] = new Variant("player name");
attributes[1] = new Variant(player_age);
The following example shows how the variant might be implemented.
struct Variant
{
enum Type
{
INT,
STRINGPTR
};
Type type_;
union
{
int int_;
const char* stringptr_;
} data_;
explicit Variant(int data) : type_(INT)
{
data_.int_ = data;
}
explicit Variant(const char *data) : type_(STRINGPTR)
{
data_.stringptr_ = data;
}
Type getType() const { return type_; }
int getIntValue() const
{
if(type_ != INT)
throw std::runtime_error("Variant is not an int");
return data_.int_;
}
const char *getStringPtr() const
{
if(type_ != STRINGPTR)
throw std::runtime_error("Variane is not a string");
return data_.stringptr_;
}
};
int main()
{
Variant intval(1);
Variant stringval("hello");
std::cout << intval.getIntValue() << std::endl;
std::cout << stringval.getStringPtr() << std::endl;
}

Insert an array of tables into one table SQLite C/C++

I made my own database format, and it sadly required too much memory and the size of it got horrendous and upkeep was horrible.
So I'm looking for a way to store an array of a struct that's in an object into a table.
I'm guessing I need to use a blob, but all other options are welcome. An easy way to implement a blob would be helpful as well.
I've attached my saving code and related structures(Updated from my horrible post earlier)
#include "stdafx.h"
#include <string>
#include <stdio.h>
#include <vector>
#include "sqlite3.h"
using namespace std;
struct PriceEntry{
float cardPrice;
string PriceDate;
int Edition;
int Rarity;
};
struct cardEntry{
string cardName;
long pesize;
long gsize;
vector<PriceEntry> cardPrices;
float vThreshold;
int fav;
};
vector<cardEntry> Cards;
void FillCards(){
int i=0;
int j=0;
char z[32]={0};
for(j=0;j<3;j++){
cardEntry tmpStruct;
sprintf(z, "Card Name: %d" , i);
tmpStruct.cardName=z;
tmpStruct.vThreshold=1.00;
tmpStruct.gsize=0;
tmpStruct.fav=1;
for(i=0;i<3;i++){
PriceEntry ss;
ss.cardPrice=i+1;
ss.Edition=i;
ss.Rarity=i-1;
sprintf(z,"This is struct %d", i);
ss.PriceDate=z;
tmpStruct.cardPrices.push_back(ss);
}
tmpStruct.pesize=tmpStruct.cardPrices.size();
Cards.push_back(tmpStruct);
}
}
int SaveCards(){
// Create an int variable for storing the return code for each call
int retval;
int CardCounter=0;
int PriceEntries=0;
char tmpQuery[256]={0};
int q_cnt = 5,q_size = 256;
sqlite3_stmt *stmt;
sqlite3 *handle;
retval = sqlite3_open("sampledb.sqlite3",&handle);
if(retval)
{
printf("Database connection failed\n");
return -1;
}
printf("Connection successful\n");
//char create_table[100] = "CREATE TABLE IF NOT EXISTS users (uname TEXT PRIMARY KEY,pass TEXT NOT NULL,activated INTEGER)";
char create_table[] = "CREATE TABLE IF NOT EXISTS Cards (CardName TEXT, PriceNum NUMERIC, Threshold NUMERIC, Fav NUMERIC);";
retval = sqlite3_exec(handle,create_table,0,0,0);
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(handle) );
for(CardCounter=0;CardCounter<Cards.size();CardCounter++){
char Query[512]={0};
for(PriceEntries=0;PriceEntries<Cards[CardCounter].cardPrices.size();PriceEntries++){
//Here is where I need to find out the process of storing the vector of PriceEntry for Cards then I can modify this loop to process the data
}
sprintf(Query,"INSERT INTO Cards VALUES('%s', %d, %f, %d)",
Cards[CardCounter].cardName.c_str(),
Cards[CardCounter].pesize,
Cards[CardCounter].vThreshold,
Cards[CardCounter].fav); //My insert command
retval = sqlite3_exec(handle,Query,0,0,0);
if(retval){
printf( "Could not prepare statement: %s\n", sqlite3_errmsg(handle) );
}
}
// Insert first row and second row
sqlite3_close(handle);
return 0;
}
I tried googling but my results didn't suffice.
You have two types here: Cards and PriceEntries. And for each Card there can be many PriceEntries.
You can store Cards in one table, one Card per row. But you're puzzled about how to store the PriceEntries, right?
What you'd normally do here is have a second table for PriceEntries, keyed off a unique column (or columns) of the Cards table. I guess the CardName is unique to each card? Let's go with that. So your PriceEntry table would have a column CardName, followed by columns of PriceEntry information. You'll have a row for each PriceEntry, even if there are duplicates in the CardName column.
The PriceEntry table might look like:
CardName | Some PE value | Some other PE value
Ace | 1 | 1
Ace | 1 | 5
2 | 2 | 3
and so on. So when you want to find the array of PriceEntries for a card, you'd do
select * from PriceEntry where CardName = 'Ace'
And from the example data above you'd get back 2 rows, which you could shove into an array (if you wanted to).
No need for BLOBs!
This is a simple serialization and deserialization system. The class PriceEntry has been extended with serialization support (very simply). Now all you have to do is serialize a PriceEntry (or a set of them) to binary data and store it in a blob column. Later on, you get the blob data and from that deserialize a new PriceEntry with the same values. An example of how it is used is given at the bottom. Enjoy.
#include <iostream>
#include <vector>
#include <string>
#include <cstring> // for memcpy
using std::vector;
using std::string;
// deserialization archive
struct iarchive
{
explicit iarchive(vector<unsigned char> data)
: _data(data)
, _cursor(0)
{}
void read(float& v) { read_var(v); }
void read(int& v) { read_var(v); }
void read(size_t& v) { read_var(v); }
void read(string& v) { read_string(v); }
vector<unsigned char> data() { return _data; }
private:
template <typename T>
void read_var(T& v)
{
// todo: check that the cursor will not be past-the-end after the operation
// read the binary data
std::memcpy(reinterpret_cast<void*>(&v), reinterpret_cast<const void*>(&_data[_cursor]), sizeof(T));
// advance the cursor
_cursor += sizeof(T);
}
inline
void
read_string(string& v)
{
// get the array size
size_t sz;
read_var(sz);
// get alignment padding
size_t padding = sz % 4;
if (padding == 1) padding = 3;
else if (padding == 3) padding = 1;
// todo: check that the cursor will not be past-the-end after the operation
// resize the string
v.resize(sz);
// read the binary data
std::memcpy(reinterpret_cast<void*>(&v[0]), reinterpret_cast<const void*>(&_data[_cursor]), sz);
// advance the cursor
_cursor += sz + padding;
}
vector<unsigned char> _data; // archive data
size_t _cursor; // current position in the data
};
// serialization archive
struct oarchive
{
void write(float v) { write_var(v); }
void write(int v) { write_var(v); }
void write(size_t v) { write_var(v); }
void write(const string& v) { write_string(v); }
vector<unsigned char> data() { return _data; }
private:
template <typename T>
void write_var(const T& v)
{
// record the current data size
size_t s(_data.size());
// enlarge the data
_data.resize(s + sizeof(T));
// store the binary data
std::memcpy(reinterpret_cast<void*>(&_data[s]), reinterpret_cast<const void*>(&v), sizeof(T));
}
void write_string(const string& v)
{
// write the string size
write(v.size());
// get alignment padding
size_t padding = v.size() % 4;
if (padding == 1) padding = 3;
else if (padding == 3) padding = 1;
// record the data size
size_t s(_data.size());
// enlarge the data
_data.resize(s + v.size() + padding);
// store the binary data
std::memcpy(reinterpret_cast<void*>(&_data[s]), reinterpret_cast<const void*>(&v[0]), v.size());
}
vector<unsigned char> _data; /// archive data
};
struct PriceEntry
{
PriceEntry()
{}
PriceEntry(iarchive& in) // <<< deserialization support
{
in.read(cardPrice);
in.read(PriceDate);
in.read(Edition);
in.read(Rarity);
}
void save(oarchive& out) const // <<< serialization support
{
out.write(cardPrice);
out.write(PriceDate);
out.write(Edition);
out.write(Rarity);
}
float cardPrice;
string PriceDate;
int Edition;
int Rarity;
};
int main()
{
// create a PriceEntry
PriceEntry x;
x.cardPrice = 1;
x.PriceDate = "hi";
x.Edition = 3;
x.Rarity = 0;
// serialize it
oarchive out;
x.save(out);
// create a deserializer archive, from serialized data
iarchive in(out.data());
// deserialize a PriceEntry
PriceEntry y(in);
std::cout << y.cardPrice << std::endl;
std::cout << y.PriceDate << std::endl;
std::cout << y.Edition << std::endl;
std::cout << y.Rarity << std::endl;
}