Javascript QDatastream (de)serializer.
List of types handled for (de)serialization: QBool, QShort, QInt, QInt64, QUInt, QUInt64, QDouble, QMap, QList, QString, QVariant, QStringList, QByteArray, QUserType, QDateTime, QTime, QChar, QInvalid
Getting Started
Install the module with npm install node-qtdatastream --production,
or npm install node-qtdatastream for development purpose.
Documentation
Type inference
Javascript types can be automatically converted to Qt Types, and here is the default behavior
javascript to QClass
| javascript | QClass |
|---|---|
| string | QString |
| number | QUInt (this can be overloaded) |
| boolean | QBool |
| Array | QList<QVariant<?>> |
| Date | QDateTime |
| Map | QMap<QString, QVariant<?>> |
| Object | QMap<QString, QVariant<?>> |
You can always force any type to be coerced to any Qt type
const { QByteArray } = require('qtdatastream').types;
const s = "hello"; // If given to the writer, it will be coerced to QString
const qbytearray = QByteArray.from(s); // This will write the same string but as a QByteArrayNB: you can change default behavior for number
const { QVariant, Types } = require('qtdatastream').types;
const n = 1; // Would be written as QUInt
QVariant.coerceNumbersTo(Types.DOUBLE); // Will now write any number as QDoubleQClass to javascript
Qt Types are also converted to native javascript type automatically upon reading
| QClass | javascript |
|---|---|
| QString | string |
| QUInt | number |
| QInt | number |
| QUInt64 | number |
| QInt64 | number |
| QDouble | number |
| QShort | number |
| QBool | number |
| QList | Array |
| QStringList | Array<string> |
| QByteArray | Buffer |
| QMap | Object |
| QUserType | Object |
| QDateTime | Date |
| QTime | number |
| QChar | string |
| QInvalid | undefined |
QUserType special treatment
QUserType are special types defined by user (QVariant::UserType).
QUserType are defined like this <size:uint32><bytearray of size>. bytearray
can be casted to string (but it is not a string as intended by Qt,
because it is UTF8 and not UTF16) : bytearray.toString(). The resulting string
is the QUserType key.
Reader
The Reader use an internal mechanism to know which parser must be used for each QUserType. They are defined like this:
const { QUserType } = require('qtdatastream').types;
QUserType.register("NetworkId", qtdatastream.Types.INT); //NetworkId here is our keyThis tell the reader to decode NetworkId bytearray like and INT. But those
structures can be much more complicated:
const { QUserType } = require('qtdatastream').types;
QUserType.register("BufferInfo", [
{id: qtdatastream.Types.INT},
{network: qtdatastream.Types.INT},
{type: qtdatastream.Types.SHORT},
{group: qtdatastream.Types.INT},
{name: qtdatastream.Types.BYTEARRAY}
]);The bytearray corresponding to this structure look like this :
<int32><int32><int16><int32><qbytearray>The whole new type will be put in a new Object, the id key will contain the first
<int32>, the network key will contain the second <int32>, etc.
The definition is contained into an array to force a parsing order (here, id will
always be the first <int32> block).
UserTypes can also be nested, by specifying the usertype name instead of Qt type :
QUserType.register("BufferInfoContainer", [
{id: qtdatastream.Types.INT},
{bufferInfo: "BufferInfo"} // here we reference the BufferInfo QUserType
]);Keep in mind that if a usertype X references usertype Y, Y should be declared before X.
Writer
Custom usertypes can be defined as for Reader, with the help of QUserType.register method.
Writing UserType is done as follow:
const { Socket } = require('qtdatastream').socket;
const { QUserType } = require('qtdatastream').types;
const qtsocket = new Socket(myRealSocket);
const data = {
"BufferInfo": new QUserType("BufferInfo", {
id: 2,
network: 4,
type: 5,
group: 1,
name: "BufferInfo name"
})
});
qtsocket.write(data);Some more examples can be found in test folder.
ES6/7
ES7 decorators can be used to simplify serializable data representation
const { types: { QUserType, QString, QUInt, Types }, serialization: { Serializable, serialize } } = require('qtdatastream');
// Register usertype
QUserType.register('Network::Server', Types.MAP);
@Serializable('Network::Server')
export class Server {
@serialize(QString, {in: 'HostIn', out: 'HostOut'))
host;
@serialize(QUInt, 'Port')
port = 6667;
@serialize(QUInt)
sslVersion = 0;
constructor(args) {
this.blob = true; // will not be serialized at export
Object.assign(this, args);
}
}
const parsedUserType = {
HostIn: 'myHost',
Port: 1234
}; // This usually comes from qtsocket
const server = new Server(parsedUserType);
// server == {
// host: 'myHost',
// port: 1234,
// sslVersion: 0
// }
// This will call server.export() method before sending to the server,
// which exports the object as dictated by Server class and 'Network::Server' usertype
qtsocket.write(server)Serializable parameter (usertype) is optionnal. If unspecified, it will be exported as a QMap.
If Serializable class implements _export method, the return of this function will be used
instead of object own attributes.
@Serializable()
export class Server {
_export() {
return {
'a': 'b'
};
}
}Example
const { Socket } = require('qtdatastream').socket;
const { QUserType } = require('qtdatastream').types;
const net = require('net');
var client = net.Socket();
// Connect to a Qt socket
// and write something into the socket
client.connect(65000, "domain.tld", function(){
const qtsocket = new Socket(client);
// Here data is the already parsed response
qtsocket.on('data', function(data) {
//...
});
// Write something to the socket
qtsocket.write({
"AString": "BString",
"CString": 42
});
});Debugging
Debug mode can be activated by setting environment variable DEBUG in your shell before launching your program:
export DEBUG="qtdatastream:*"License
Copyright (c) 2017 Joël Charles Licensed under the MIT license.
