QMap Serialization in Qt - qt

I have a 2 classes: valueNode and keyNode. Both of these classes have 2 private members. Now I create a QMap< keyNode , valueNode >. for this I override operator<(). I want to serialize this QMap but I don't know how.

QMap<QString, QString> map;
map.insert("Hello", " World!");
QByteArray data;
QDataStream * stream = new QDataStream(&data, QIODevice::WriteOnly);
(*stream) << map;
delete stream;
// Now QByteArray should have the map as serialized data.
This should work.
You might wonder about the new and delete madness, but there is a reason: there is no way to flush the data from the stream to the bytearray, except by deconstructing the stream. Or maybe there is, give me a comment if I'm wrong.
Edit:
Oh yeah, forgot one thing.
You need to make these functions:
QDataStream & operator << (QDataStream & out, const MyClass & object);
QDataStream & operator >> (QDataStream & in, MyClass & object);
Introduce them in the headers of your classes and implement in the cpp file of that class.
// MyClass.h
MyClass
{
...
};
QDataStream & operator << ...
QDataStream & operator >> ...
Note that it must be a global function and not a member function.
Note that you must create a pair for each of your classes.

Store it in a QVariant which you can then use a QDataStream to read/write it.

Related

QMetaType::convert failed

I'm trying to use QMetaType::convert to convert a QJsonValue to another dynamic type. At first, I tested the following code with the dynamic type setting to QString, the conversion was failed.
QJsonValue value("test");
QString string;
if (!QMetaType::convert(&value, QMetaType::QJsonValue, &string, QMetaType::QString))
{
qDebug() << "failed";
}
Then, I found this static method to check whether the meta system has a registered conversion between two meta types.
qDebug() << QMetaType::hasRegisteredConverterFunction(QMetaType::QJsonValue, QMetaType::QString);
unfortunately, the result was false. Maybe QJsonValue is so complex that the conversion from QJsonValue to QString is not supported. Finally, I tried this, and the result was still false:
qDebug() << QMetaType::hasRegisteredConverterFunction(QMetaType::Int, QMetaType::Int);
It's odd, seems to be, Qt dose not implement the converter functions between basic meta types. And, users can't use QMetaType::registerConverter to register converter function between two basic meta types.
I still can't believe that Qt dosen't implement conversions between basic meta types, is there any initializtion or .pro setting I missed?
I guess you have the QMetaType system and the QVariant class to encapsulate Qt data types on the one hand, and QJsonValue to encapsulate a value in JSON on the other hand.
QMetaType::convert can deal with QVariant data only. What you can do is to extract the QVariant from your QJsonValue and then use the QMetaType system to convert your data you know being a QString.
// Your code
QJsonValue value("test");
QString string ;
if (!QMetaType::convert(&value, QMetaType::QJsonValue, &string, QMetaType::QString))
{
qDebug() << "failed";
}
// Extract the QVariant data
QVariant variant = value.toVariant() ;
// Two way to test conversion between QVariant and a type
qDebug() << "canConvert template:" << variant.canConvert<QString>() << endl
<< "canConvert parameter:" << variant.canConvert( QMetaType::QString ) ;
if( variant.canConvert<QString>() )
{
// Convert using QVariant methods
qDebug() << "QVariant toString:" << variant.toString() ;
// Convert Using QJsonValue methods
qDebug() << "QJsonValue toString:" << value.toString() ; // It's just a string representation of the data, not the actual data
}
outputs :
failed
canConvert template: true
canConvert parameter: true
QVariant toString: "test"
QJsonValue toString: "test"
PS: the QJsonValue::Type : String is different from the QVariant::Type : String (QMetaType::QString) so there is no relationship between them.

How to 'steal' data from QByteArray without copy

Is it possible to take pointer to QByteArray's internal T* data and destroy QByteArray itself so that the pointer remains unreleased? I would like to use it in the something similar to the following scenario:
const char* File::readAllBytes(const String& path, u64& outCount) {
QFile file(*static_cast<QString*>(path.internal()));
outCount = static_cast<u64>(file.size());
if (!file.open(QIODevice::ReadOnly)) gException("Failed to open file");
const QByteArray array = file.readAll();
return array.steal();
}
No, you can't steal QByteArray's pointer unless it has been constructed with QByteArray::fromRawData, which is not the case. However you can create char array manually and read data from file to it using QFile::read(char * data, qint64 maxSize). You will then decide where to pass the pointer and when to delete[] it.
Note that this is not considered good practice. You should use managed allocations whenever you can, and Qt provides enough to cover most of possible use cases. You should not do this unless you're trying to do something really low-level.
Note that many of Qt classes, including QString and QByteArray, use copy-on-write strategy, so you generally should not be afraid of copying them and passing them to another context.
No, but you can easily sidestep the problem by not using QByteArray:
const char* File::readAllBytes(const String& path, u64& outCount) {
QFile file(*static_cast<QString*>(path.internal()));
if (!file.open(QIODevice::ReadOnly)) return nullptr;
auto N = file.bytesAvailable();
char *data = malloc(N);
outCount = file.read(data, N);
return data;
}
The solution above also assumes that the consumer of your data is aware of the need to free the data.
Alas, the manual memory management called for with such an API is a bad idea. If you wish not to use Qt classes in your API, you should be using std::vector<char> instead:
std::vector<char> File::readAllBytes(const String& path) {
std::vector<char> result;
QFile file(*static_cast<QString*>(path.internal()));
if (!file.open(QIODevice::ReadOnly)) return result;
result.resize(file.bytesAvailable());
auto count = file.read(result.data(), result.size());
result.resize(count);
return result;
}
I smell that String is some sort of a framework-independent string wrapper. Perhaps you could settle on std::u16string to carry the same UTF16 data as QString would.

Does QBuffer write data in system dependent byte order?

The documentation says that QDataStream writes data in system independent way, but it says nothing about QBuffer. I develop a program that saves data in a file like this:
QByteArray a;
QBuffer b(&a);
b.open(QIODevide::WriteOnly);
quint32 x = 1;
b.write((char*)&x, sizeof(x));
b.close();
QFile f(...);
f.open(QIODevide::WriteOnly);
f.write(a.constData(), a.size());
f.close();
, and i want this file can be read in any other OS (win, linux, Mac OS). Will this code work or i must use QDataStream instead?
The QBuffer documentation says :
The QBuffer class provides a QIODevice interface for a QByteArray.
ie it is only a QByteArray underneath. On the other hand a QByteArray is portable because as long as you see the data as an array of byte and write one byte at a time you are fine. Your code will work:
When you say
I want this file to be read in any other OS
Is your file used by your program only or will it be used by other applications in the system? QDataStream provides nicer functions for I\O and you may be still able to take advantage of it.
It will be platform specific. x representation in memory depend on the endianess.It doesn't occur in the QBuffer, but when you do :
b.write((char*)&x, sizeof(x));
If you are on machines of different endianess, you will obtain different values for the resulting array by doing
char* data = &x;
qDebug()<< data[0];
qDebug()<< data[1];
qDebug()<< data[2];
qDebug()<< data[3];
Take a look at the source code of QDataStream operator
QDataStream &QDataStream::operator<<(qint32 i){
CHECK_STREAM_WRITE_PRECOND(*this)
if (!noswap) {
i = qbswap(i);
}
if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32))
q_status = WriteFailed;
return *this;
}

Not able to Seralize QHash to DataStream

while leaning about QHash and serializing QHash to DataStream I got an error with the following code.
typedef QHash <quint32,QString> hashtype1;
typedef QHash <QLocale::Language,hashtype1> hashtype;
hashtype1 hash;
hash.insert(1, "Key1");
hash.insert(2, "Key2");
hashtype hash1;
hash1.insert(QLocale::English, hash);
hash1.insert(QLocale::French, hash);
QByteArray ba;
QByteArray ba1;
QDataStream ds(&ba, QIODevice::ReadWrite);
QDataStream ds1(&ba1, QIODevice::ReadWrite);
ds << hash;
ds1 << hash1;
qDebug() << ds.device()->readAll();
ds.device()->reset();
ds1.device()->reset();
hashtype1 hashcopy;
ds >> hashcopy;
hashtype hash1copy;
ds1 >> hash1copy;
The last statement is giving an error saying
/usr/include/qt4/QtCore/qdatastream.h:362: error: no match for ‘operator>>’ in ‘in >> k’
I am not able to correct this..
Am I doing somthing wrong??
How can I correct this?
The problem is that there are no QDataStream operators for QLocale::Language. When streaming out this works because it gets automatically converted to an integer type. It can't do this for streaming in. So you will either need to change your QHash to use a different template parameter for the key or write streaming operators for QLocale::Language (which should be trivial, you just need to cast it to/from int).

convert std::ostream to some array?

I have a legacy library that takes data from hardware and writes it to ostream.
The method looks like following :
int sensors(ostream*) const;
I am not skilled enough in Ancient Ways. How to convert this data to QByteArray? Or, at least, to char array of known size?
I would have solved it myself, but there is an additional problem: the data in ostream seem to be arbitrary length and have several arbitrary '\0' symbols, so you can't count on it being null-terminated.
I think this is what OrcunC was getting at:
std::stringstream s;
sensors( &s );
QByteArray( s.str().data(), (int) s.str().size() );
... but hopefully more clear :). See also std::stringstream and std::string for information on the classes/member functions used here. By the way, note that I am using str().data(), not str().c_str() -- I'm being really careful to handle those \0 characters, and I'm not assuming NULL termination.
I have not tried it, but you need something like this :
ostream s (ios::out | ios::binary);
//..Populate the stream
//Convert it to string. string can hold \0 values too.
string str = s.str ();
QByteArray ba (str.data (),str.size ());
You can subclass std::ostream and use an object of the subclass to collect the bytes into your QByteArray.
/**
* This helper class collects bytes that are passed to it
* into a QByteArray.
*
* After https://stackoverflow.com/a/19933011/316578
*/
class QByteArrayAppender : public std::ostream, public std::streambuf {
private:
QByteArray &m_byteArray;
public:
QByteArrayAppender(QByteArray &byteArray)
: std::ostream(this),
std::streambuf(),
m_byteArray(byteArray) {
}
int overflow(int c) {
m_byteArray.append(static_cast<char>(c));
return 0;
}
};
This avoids going via an std::string, which is an extra copy of the byte array.

Resources