Преглед изворни кода

添加: json序列反序列
修改: 已知bug

zhuizhu пре 9 месеци
родитељ
комит
05690f223e
100 измењених фајлова са 7278 додато и 7 уклоњено
  1. 1 1
      AvPlayer2/playercontroller.cpp
  2. 7 5
      AvPlayer2/start_play_thread.cpp
  3. 4 1
      AvPlayer2/start_play_thread.h
  4. 4 0
      LearningSmartClient.pro
  5. 100 0
      api/roomapi.cpp
  6. 72 0
      api/roomapi.h
  7. 15 0
      api/userapi.h
  8. BIN
      bin/2025-07-12-20-11-39.mp4
  9. BIN
      bin/2025-07-12-20-11-58.mp4
  10. 15 0
      jsonserializer/QtJsonSerializer
  11. 236 0
      jsonserializer/cborserializer.cpp
  12. 195 0
      jsonserializer/cborserializer.h
  13. 39 0
      jsonserializer/cborserializer_p.h
  14. 83 0
      jsonserializer/exception.cpp
  15. 79 0
      jsonserializer/exception.h
  16. 22 0
      jsonserializer/exception_p.h
  17. 43 0
      jsonserializer/exceptioncontext.cpp
  18. 31 0
      jsonserializer/exceptioncontext_p.h
  19. 143 0
      jsonserializer/jsonserializer.cpp
  20. 147 0
      jsonserializer/jsonserializer.h
  21. 92 0
      jsonserializer/jsonserializer.pri
  22. 21 0
      jsonserializer/jsonserializer_p.h
  23. 275 0
      jsonserializer/metawriters.cpp
  24. 303 0
      jsonserializer/metawriters.h
  25. 78 0
      jsonserializer/metawriters_p.h
  26. 14 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QByteArray.cpp
  27. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QCborArray.cpp
  28. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QCborMap.cpp
  29. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QCborValue.cpp
  30. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QChar.cpp
  31. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QDate.cpp
  32. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QDateTime.cpp
  33. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QJsonArray.cpp
  34. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QJsonObject.cpp
  35. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QJsonValue.cpp
  36. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QLine.cpp
  37. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QLineF.cpp
  38. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QLocale.cpp
  39. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QMimeType.cpp
  40. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QObject_.cpp
  41. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QPoint.cpp
  42. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QPointF.cpp
  43. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QRect.cpp
  44. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QRectF.cpp
  45. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QRegularExpression.cpp
  46. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QSize.cpp
  47. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QSizeF.cpp
  48. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QString.cpp
  49. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QTime.cpp
  50. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QUrl.cpp
  51. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QUuid.cpp
  52. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_QVersionNumber.cpp
  53. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_bool.cpp
  54. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_char.cpp
  55. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_double.cpp
  56. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_float.cpp
  57. 99 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_hook.cpp
  58. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_int.cpp
  59. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_long.cpp
  60. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_qlonglong.cpp
  61. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_qulonglong.cpp
  62. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_short.cpp
  63. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_signed_char.cpp
  64. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_uchar.cpp
  65. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_uint.cpp
  66. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_ulong.cpp
  67. 13 0
      jsonserializer/qjsonconverterreg/qjsonconverterreg_ushort.cpp
  68. 87 0
      jsonserializer/qjsonreggen.pri
  69. 87 0
      jsonserializer/qjsonreggen.py
  70. 32 0
      jsonserializer/qtjsonserializer_global.h
  71. 337 0
      jsonserializer/qtjsonserializer_helpertypes.h
  72. 9 0
      jsonserializer/qtjsonserializerversion.h
  73. 790 0
      jsonserializer/serializerbase.cpp
  74. 788 0
      jsonserializer/serializerbase.cpp~RFc998f2.TMP
  75. 351 0
      jsonserializer/serializerbase.h
  76. 149 0
      jsonserializer/serializerbase_p.h
  77. 167 0
      jsonserializer/typeconverter.cpp
  78. 176 0
      jsonserializer/typeconverter.h
  79. 74 0
      jsonserializer/typeconverters/bitarrayconverter.cpp
  80. 24 0
      jsonserializer/typeconverters/bitarrayconverter_p.h
  81. 105 0
      jsonserializer/typeconverters/bytearrayconverter.cpp
  82. 24 0
      jsonserializer/typeconverters/bytearrayconverter_p.h
  83. 118 0
      jsonserializer/typeconverters/cborconverter.cpp
  84. 22 0
      jsonserializer/typeconverters/cborconverter_p.h
  85. 125 0
      jsonserializer/typeconverters/datetimeconverter.cpp
  86. 26 0
      jsonserializer/typeconverters/datetimeconverter_p.h
  87. 143 0
      jsonserializer/typeconverters/enumconverter.cpp
  88. 30 0
      jsonserializer/typeconverters/enumconverter_p.h
  89. 169 0
      jsonserializer/typeconverters/gadgetconverter.cpp
  90. 21 0
      jsonserializer/typeconverters/gadgetconverter_p.h
  91. 229 0
      jsonserializer/typeconverters/geomconverter.cpp
  92. 52 0
      jsonserializer/typeconverters/geomconverter_p.h
  93. 150 0
      jsonserializer/typeconverters/legacygeomconverter.cpp
  94. 48 0
      jsonserializer/typeconverters/legacygeomconverter_p.h
  95. 87 0
      jsonserializer/typeconverters/listconverter.cpp
  96. 22 0
      jsonserializer/typeconverters/listconverter_p.h
  97. 60 0
      jsonserializer/typeconverters/localeconverter.cpp
  98. 23 0
      jsonserializer/typeconverters/localeconverter_p.h
  99. 93 0
      jsonserializer/typeconverters/mapconverter.cpp
  100. 22 0
      jsonserializer/typeconverters/mapconverter_p.h

+ 1 - 1
AvPlayer2/playercontroller.cpp

@@ -634,7 +634,7 @@ bool PlayerController::startPlayThread()
     if (m_beforePlayThread)
         return false;
 
-    m_beforePlayThread = std::make_unique<StartPlayThread>(this);
+    m_beforePlayThread = std::make_unique<StartPlayThread>(this, this);
     connect(m_beforePlayThread.get(),
             &StartPlayThread::audio_device_init,
             this,

+ 7 - 5
AvPlayer2/start_play_thread.cpp

@@ -8,19 +8,21 @@
 // ***********************************************************/
 
 #include "start_play_thread.h"
+
 #include "AVPlayer2/video_state.h"
+
 #include "playercontroller.h"
 
-StartPlayThread::StartPlayThread(QObject* parent)
+StartPlayThread::StartPlayThread(PlayerController* playerController, QObject* parent)
     : QThread(parent)
+    , m_playerController(playerController)
 {}
 
 StartPlayThread::~StartPlayThread() {}
 
 void StartPlayThread::run()
 {
-    PlayerController* pParent = (PlayerController*) parent();
-    assert(pParent);
+    assert(m_playerController);
     bool ret = false;
 
 #if !NDEBUG
@@ -32,12 +34,12 @@ void StartPlayThread::run()
     //pParent->volume_settings(false);
     AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16; // play out format
 
-    VideoStateData* pVideoStateData = pParent->videoStateData();
+    VideoStateData* pVideoStateData = m_playerController->videoStateData();
     if (pVideoStateData) {
         AVCodecContext* pAudio = pVideoStateData->get_contex(AVMEDIA_TYPE_AUDIO);
         VideoState* pState = pVideoStateData->get_state();
         if (pAudio) {
-            AudioPlayThread* pThread = pParent->audioPlayThread();
+            AudioPlayThread* pThread = m_playerController->audioPlayThread();
             if (pThread) {
                 ret = pThread->init_device(pAudio->sample_rate,
                                            pAudio->ch_layout.nb_channels,

+ 4 - 1
AvPlayer2/start_play_thread.h

@@ -2,16 +2,19 @@
 
 #include <QThread>
 
+class PlayerController;
+
 class StartPlayThread : public QThread
 {
     Q_OBJECT
 
 public:
-    explicit StartPlayThread(QObject* parent = Q_NULLPTR);
+    explicit StartPlayThread(PlayerController *playerController, QObject *parent = Q_NULLPTR);
     ~StartPlayThread();
 signals:
     void audio_device_init(bool ret);
 
 protected:
     void run() override;
+    PlayerController *m_playerController;
 };

+ 4 - 0
LearningSmartClient.pro

@@ -14,6 +14,7 @@ QT += opengl
 SOURCES += \
     api/chatapi.cpp \
     api/loginapi.cpp \
+    api/roomapi.cpp \
     api/userapi.cpp \
     appevent.cpp \
     main.cpp \
@@ -43,6 +44,7 @@ SOURCES += \
 HEADERS += \
     api/chatapi.h \
     api/loginapi.h \
+    api/roomapi.h \
     api/userapi.h \
     appevent.h \
     mainwindow.h \
@@ -82,6 +84,8 @@ include($$PWD/AvPlayer2/AvPlayer2.pri)
 include($$PWD/qwindowkit/qwindowkit.pri)
 include($$PWD/fmt.pri)
 include($$PWD/qtpromise/qtpromise.pri)
+include($$PWD/jsonserializer/jsonserializer.pri)
+
 
 INCLUDEPATH+="E:/AAA/ffmpeg-7.0.2-full_build-shared/include"
 LIBS+="-LE:/AAA/ffmpeg-7.0.2-full_build-shared/lib"

+ 100 - 0
api/roomapi.cpp

@@ -0,0 +1,100 @@
+#include "roomapi.h"
+#include "qjsonobject.h"
+
+#include "qtjsonserializer_global.h"
+#include "util/jsonmapper.h"
+#include <cstddef>
+#include <utility>
+
+// std::optional<QString> id;          // 房间当前id
+// std::optional<QString> name;        // 房间名
+// std::optional<QString> description; // 房间的描述信息
+// std::optional<int> maxUsers;        // 最大用户数
+// std::optional<qint64> status;       // 1: normal 2: ban | 状态 1 正常 2 禁用
+// std::optional<qint64> createdAt;    // 创建日期
+// std::optional<qint64> updatedAt;    // 修改日期
+// std::optional<QString> createdId;   // 创建者
+// std::optional<QString> ownerId;     // 房主ID,关联用户表
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+// 为 Qt 5 注册 std::optional 类型
+Q_DECLARE_METATYPE(std::optional<QString>)
+Q_DECLARE_METATYPE(std::optional<int>)
+Q_DECLARE_METATYPE(std::optional<qint64>)
+
+// 对于 QString 类型还需要流运算符
+QDataStream &operator<<(QDataStream &out, const std::optional<QString> &opt)
+{
+    out << static_cast<bool>(opt.has_value());
+    if (opt)
+        out << *opt;
+    return out;
+}
+
+QDataStream &operator>>(QDataStream &in, std::optional<QString> &opt)
+{
+    bool hasValue;
+    in >> hasValue;
+    if (hasValue) {
+        QString value;
+        in >> value;
+        opt = value;
+    } else {
+        opt = std::nullopt;
+    }
+    return in;
+}
+
+void initRoomType()
+{
+    QtJsonSerializer::registerTypes();
+    QtJsonSerializer::JsonSerializer::registerOptionalConverters<QString>();
+    QtJsonSerializer::JsonSerializer::registerListConverters<RoomInfo>();
+    qRegisterMetaType<RoomInfo>("RoomInfo");
+    qRegisterMetaType<RoomListData>("RoomListData");
+    qRegisterMetaType<std::optional<QString>>("std::optional<QString>");
+}
+#endif
+
+RoomListData::RoomListData(const QJsonObject &object)
+{
+    // 正式开始
+    QtJsonSerializer::JsonSerializer serializer;
+    // serializer.setAllowDefaultNull(true);
+    RoomListData roomList;
+    try {
+        roomList = serializer.deserialize<RoomListData>(object);
+        data = roomList.data;
+        total = roomList.total;
+    } catch (const QtJsonSerializer::Exception &e) {
+        qDebug() << "Deserialization error:" << e.what();
+    }
+}
+
+QList<RoomInfo> RoomListData::getRooms() const
+{
+    return data;
+}
+
+QFuture<HttpResponse> getRoomListApi(const RoomInfo &roomInfo, int page, int pageSize)
+{
+    QJsonObject jsonData;
+    QJsonValue value = JsonMapper::toJson(roomInfo);
+
+    jsonData["page"] = page;
+    jsonData["pageSize"] = pageSize;
+    if (value.isObject()) {
+        QJsonObject nested = value.toObject();
+        for (const QString &key : nested.keys()) {
+            if (!nested[key].isNull()) {
+                jsonData[key] = nested[key];
+            }
+        }
+    }
+
+    // 活驴
+    qDebug() << jsonData;
+    QJsonDocument doc(jsonData);
+
+    return TC::RequestClient::globalInstance()->postAsync("/room/list", doc);
+}

+ 72 - 0
api/roomapi.h

@@ -0,0 +1,72 @@
+#ifndef ROOMAPI_H
+#define ROOMAPI_H
+
+#include "network/networkaccessmanager.h"
+#include "qjsonarray.h"
+
+#include <QMetaType>
+#include <QString>
+#include <QStringList>
+
+#include <optional>
+
+class RoomInfo
+{
+    Q_GADGET
+    Q_PROPERTY(std::optional<QString> id MEMBER id)
+    Q_PROPERTY(std::optional<QString> name MEMBER name)
+    Q_PROPERTY(std::optional<QString> description MEMBER description)
+    Q_PROPERTY(std::optional<int> maxUsers MEMBER maxUsers)
+    Q_PROPERTY(std::optional<qint64> status MEMBER status)
+    Q_PROPERTY(std::optional<qint64> createdAt MEMBER createdAt)
+    Q_PROPERTY(std::optional<qint64> updatedAt MEMBER updatedAt)
+    Q_PROPERTY(std::optional<QString> createdId MEMBER createdId)
+    Q_PROPERTY(std::optional<QString> ownerId MEMBER ownerId)
+public:
+    RoomInfo() = default;
+
+    std::optional<QString> id;          // 房间当前id
+    std::optional<QString> name;        // 房间名
+    std::optional<QString> description; // 房间的描述信息
+    std::optional<int> maxUsers;        // 最大用户数
+    std::optional<qint64> status;       // 1: normal 2: ban | 状态 1 正常 2 禁用
+    std::optional<qint64> createdAt;    // 创建日期
+    std::optional<qint64> updatedAt;    // 修改日期
+    std::optional<QString> createdId;   // 创建者
+    std::optional<QString> ownerId;     // 房主ID,关联用户表
+
+    friend bool operator==(const RoomInfo &lhs, const RoomInfo &rhs)
+    {
+        return lhs.id == rhs.id && lhs.name == rhs.name && lhs.description == rhs.description
+               && lhs.maxUsers == rhs.maxUsers && lhs.status == rhs.status
+               && lhs.createdAt == rhs.createdAt && lhs.updatedAt == rhs.updatedAt
+               && lhs.createdId == rhs.createdId && lhs.ownerId == rhs.ownerId;
+    }
+};
+Q_DECLARE_METATYPE(RoomInfo)
+
+class RoomListData
+{
+    Q_GADGET
+    Q_PROPERTY(qint64 total MEMBER total)
+    Q_PROPERTY(QList<RoomInfo> data MEMBER data)
+
+public:
+    RoomListData() {}
+    RoomListData(const QJsonObject &object);
+
+    RoomListData(const RoomListData &other) = default;
+    RoomListData &operator=(const RoomListData &other) = default;
+
+    QList<RoomInfo> getRooms() const;
+
+private:
+    qint64 total = 0;
+    QList<RoomInfo> data;
+};
+Q_DECLARE_METATYPE(RoomListData)
+
+QFuture<HttpResponse> getRoomListApi(const RoomInfo &roomInfo = RoomInfo(),
+                                     int page = 1,
+                                     int pageSize = 100);
+#endif // ROOMAPI_H

+ 15 - 0
api/userapi.h

@@ -7,6 +7,21 @@
 #include <QString>
 #include <QStringList>
 
+class BaseListInfo
+{
+    Q_GADGET
+    Q_PROPERTY(qint64 total MEMBER total)
+    Q_PROPERTY(QString username MEMBER username)
+    Q_PROPERTY(QStringList roleName MEMBER roleName)
+
+public:
+    BaseListInfo() = default;
+
+    qint64 total;
+    QString username;
+    QStringList roleName;
+};
+
 class UserInfo
 {
     Q_GADGET

BIN
bin/2025-07-12-20-11-39.mp4


BIN
bin/2025-07-12-20-11-58.mp4


+ 15 - 0
jsonserializer/QtJsonSerializer

@@ -0,0 +1,15 @@
+#ifndef QT_QTJSONSERIALIZER_MODULE_H
+#define QT_QTJSONSERIALIZER_MODULE_H
+//#include <QtJsonSerializer/QtJsonSerializerDepends>
+#include "exception.h"
+
+
+#include "cborserializer.h"
+#include "exception.h"
+#include "jsonserializer.h"
+#include "metawriters.h"
+#include "serializerbase.h"
+#include "typeconverter.h"
+#include "typeextractors.h"
+#include "qtjsonserializerversion.h"
+#endif

+ 236 - 0
jsonserializer/cborserializer.cpp

@@ -0,0 +1,236 @@
+#include "cborserializer.h"
+#include "cborserializer_p.h"
+
+#include <cmath>
+
+#include <QtCore/QCborStreamReader>
+#include <QtCore/QCborStreamWriter>
+#include <QtCore/QtEndian>
+using namespace QtJsonSerializer;
+
+Q_LOGGING_CATEGORY(QtJsonSerializer::logCbor, "qt.jsonserializer.serializer.cbor")
+
+CborSerializer::CborSerializer(QObject *parent) :
+	SerializerBase{*new CborSerializerPrivate{}, parent}
+{
+	Q_D(CborSerializer);
+	d->typeTags = {
+		{QMetaType::QColor, static_cast<QCborTag>(CborSerializer::Color)},
+		{QMetaType::QFont, static_cast<QCborTag>(CborSerializer::Font)}
+	};
+}
+
+bool CborSerializer::handleSpecialNumbers() const
+{
+	Q_D(const CborSerializer);
+	return d->handleSpecialNumbers;
+}
+
+void CborSerializer::setTypeTag(int metaTypeId, QCborTag tag)
+{
+	Q_D(CborSerializer);
+	Q_ASSERT_X(metaTypeId != QMetaType::UnknownType, Q_FUNC_INFO, "You cannot assign a tag to QMetaType::UnknownType");
+	QWriteLocker lock{&d->typeTagsLock};
+	if (tag == TypeConverter::NoTag) {
+		d->typeTags.remove(metaTypeId);
+		qCDebug(logCbor) << "Added Type-Tag for" << QMetaTypeName(metaTypeId)
+						 << "as" << tag;
+	} else {
+		d->typeTags.insert(metaTypeId, tag);
+		qCDebug(logCbor) << "Removed Type-Tag for metaTypeId" << QMetaTypeName(metaTypeId);
+	}
+}
+
+QCborTag CborSerializer::typeTag(int metaTypeId) const
+{
+	Q_D(const CborSerializer);
+	QReadLocker lock{&d->typeTagsLock};
+	const auto tag = d->typeTags.value(metaTypeId, TypeConverter::NoTag);
+	if (tag != TypeConverter::NoTag) {
+		qCDebug(logCbor) << "Found Type-Tag for metaTypeId" << QMetaTypeName(metaTypeId)
+						 << "as" << tag;
+	} else
+		qCDebug(logCbor) << "No Type-Tag found for metaTypeId" << QMetaTypeName(metaTypeId);
+	return tag;
+}
+
+QCborValue CborSerializer::serialize(const QVariant &data) const
+{
+	return serializeVariant(data.userType(), data);
+}
+
+void CborSerializer::serializeTo(QIODevice *device, const QVariant &data, QCborValue::EncodingOptions options) const
+{
+	if (!device->isOpen() || !device->isWritable())
+		throw SerializationException{"QIODevice must be open and writable!"};
+	QCborStreamWriter writer{device};
+	serializeVariant(data.userType(), data).toCbor(writer, options);
+}
+
+QByteArray CborSerializer::serializeTo(const QVariant &data, QCborValue::EncodingOptions options) const
+{
+	return serializeVariant(data.userType(), data).toCbor(options);
+}
+
+QVariant CborSerializer::deserialize(const QCborValue &cbor, int metaTypeId, QObject *parent) const
+{
+	return deserializeVariant(metaTypeId, cbor, parent);
+}
+
+QVariant CborSerializer::deserializeFrom(QIODevice *device, int metaTypeId, QObject *parent) const
+{
+	if (!device->isOpen() || !device->isReadable())
+		throw DeserializationException{"QIODevice must be open and readable!"};
+	QCborStreamReader reader{device};
+	const auto cbor = QCborValue::fromCbor(reader);
+	if (const auto error = reader.lastError(); error.c != QCborError::NoError)
+		throw DeserializationException("Failed to read file as CBOR with error: " + error.toString().toUtf8());
+	return deserializeVariant(metaTypeId, cbor, parent);
+}
+
+QVariant CborSerializer::deserializeFrom(const QByteArray &data, int metaTypeId, QObject *parent) const
+{
+	QCborParserError error;
+	const auto cbor = QCborValue::fromCbor(data, &error);
+	if (error.error.c != QCborError::NoError)
+		throw DeserializationException("Failed to read file as CBOR with error: " + error.error.toString().toUtf8());
+	return deserializeVariant(metaTypeId, cbor, parent);
+}
+
+std::variant<QCborValue, QJsonValue> CborSerializer::serializeGeneric(const QVariant &value) const
+{
+	return serialize(value);
+}
+
+QVariant CborSerializer::deserializeGeneric(const std::variant<QCborValue, QJsonValue> &value, int metaTypeId, QObject *parent) const
+{
+	return deserialize(std::get<QCborValue>(value), metaTypeId, parent);
+}
+
+void CborSerializer::setHandleSpecialNumbers(bool handleSpecialNumbers)
+{
+	Q_D(CborSerializer);
+	if(d->handleSpecialNumbers == handleSpecialNumbers)
+		return;
+
+	d->handleSpecialNumbers = handleSpecialNumbers;
+	emit handleSpecialNumbersChanged(d->handleSpecialNumbers, {});
+}
+
+bool CborSerializer::jsonMode() const
+{
+	return false;
+}
+
+QList<int> CborSerializer::typesForTag(QCborTag tag) const
+{
+	Q_D(const CborSerializer);
+	QReadLocker lock{&d->typeTagsLock};
+	const auto keys = d->typeTags.keys(tag);
+	qCDebug(logCbor) << "Found metaTypeIds for tag" << tag
+					 << "as" << keys;
+	return keys;
+}
+
+// ------------- private implementation -------------
+
+namespace {
+
+template <typename TInt>
+bool testOverflow(const QByteArray &data) {
+	return (data.size() == static_cast<int>(sizeof(TInt))) &&
+		   (data[0] & 0x80) != 0;
+}
+
+template <typename TInt, bool Invert = false>
+TInt extract(QByteArray data) {
+	static_assert (std::is_integral_v<TInt>, "TInt must be an integer type");
+	if (data.size() < static_cast<int>(sizeof(TInt)))
+		data.prepend(QByteArray(static_cast<int>(sizeof(TInt)) - data.size(), 0));
+	if constexpr (Invert)
+		return ~qFromBigEndian<TInt>(data.data());
+	else
+		return qFromBigEndian<TInt>(data.data());
+}
+
+}
+
+QVariant CborSerializerPrivate::deserializeCborValue(int propertyType, const QCborValue &value) const
+{
+	if (handleSpecialNumbers) {
+		switch (value.tag()) {
+		case static_cast<QCborTag>(QCborKnownTags::PositiveBignum):
+			return deserializePositiveBignum(value.taggedValue().toByteArray());
+		case static_cast<QCborTag>(QCborKnownTags::NegativeBignum):
+			return deserializeNegativeBignum(value.taggedValue().toByteArray());
+		case static_cast<QCborTag>(QCborKnownTags::Decimal):
+			return deserializeDecimal(value.taggedValue().toArray());
+		case static_cast<QCborTag>(QCborKnownTags::Bigfloat):
+			return deserializeBigfloat(value.taggedValue().toArray());
+		case static_cast<QCborTag>(ExtendedTags::RationaleNumber):
+			return deserializeRationaleNumber(value.taggedValue().toArray());
+		default:
+			break;
+		}
+	}
+
+	return SerializerBasePrivate::deserializeCborValue(propertyType, value);
+}
+
+QVariant CborSerializerPrivate::deserializePositiveBignum(const QByteArray &data) const
+{
+	const auto dSize = static_cast<size_t>(data.size());
+	if (dSize > static_cast<int>(sizeof(quint64))) {
+		throw DeserializationException{"Unable to handle PositiveBignum tagged integers, bigger then " +
+									   QByteArray::number(static_cast<int>(sizeof(quint64))) +
+									   " bytes"};
+	}
+	if (dSize <= sizeof (quint8))
+		return extract<quint8>(data);
+	else if (dSize <= sizeof (quint16))
+		return extract<quint16>(data);
+	else if (dSize <= sizeof (quint32))
+		return extract<quint32>(data);
+	else
+		return QVariant::fromValue(extract<quint64>(data));
+}
+
+QVariant CborSerializerPrivate::deserializeNegativeBignum(const QByteArray &data) const
+{
+	const auto dSize = static_cast<size_t>(data.size());
+	if (dSize > static_cast<int>(sizeof(qint64)) ||  testOverflow<qint64>(data)) {
+		throw DeserializationException{"Unable to handle NegativeBignum tagged integers, bigger then " +
+									   QByteArray::number(static_cast<int>(sizeof(qint64))) +
+									   " bytes (- the first bit)"};
+	}
+	if (dSize <= sizeof (qint8) && !testOverflow<qint8>(data))
+		return QVariant::fromValue<qint8>(extract<qint8, true>(data));
+	else if (dSize <= sizeof (qint16) && !testOverflow<qint16>(data))
+		return extract<qint16, true>(data);
+	else if (dSize <= sizeof (qint32) && !testOverflow<qint32>(data))
+		return extract<qint32, true>(data);
+	else
+		return QVariant::fromValue(extract<qint64, true>(data));
+}
+
+qreal CborSerializerPrivate::deserializeDecimal(const QCborArray &data) const
+{
+	if (data.size() != 2)
+		throw DeserializationException{"Decimal tagged types must be an array with exactly two elements"};
+	return std::pow(10, data[0].toInteger()) * data[1].toInteger();
+}
+
+qreal CborSerializerPrivate::deserializeBigfloat(const QCborArray &data) const
+{
+	if (data.size() != 2)
+		throw DeserializationException{"Bigfloat tagged types must be an array with exactly two elements"};
+	return std::ldexp(data[1].toInteger(), data[0].toInteger());
+}
+
+qreal CborSerializerPrivate::deserializeRationaleNumber(const QCborArray &data) const
+{
+	if (data.size() != 2)
+		throw DeserializationException{"RationaleNumber tagged types must be an array with exactly two elements"};
+	return static_cast<long double>(data[0].toInteger()) /
+		   static_cast<long double>(data[1].toInteger());
+}

+ 195 - 0
jsonserializer/cborserializer.h

@@ -0,0 +1,195 @@
+#ifndef QTJSONSERIALIZER_CBORSERIALIZER_H
+#define QTJSONSERIALIZER_CBORSERIALIZER_H
+
+#include "qtjsonserializer_global.h"
+
+#include "jsonserializer/serializerbase.h"
+
+namespace QtJsonSerializer {
+
+class CborSerializerPrivate;
+//! A class to serialize and deserialize c++ classes to and from CBOR
+class Q_JSONSERIALIZER_EXPORT CborSerializer : public SerializerBase
+{
+	Q_OBJECT
+
+	//! If enabled, specially tagged number types will be automatically deserialized to their type
+	Q_PROPERTY(bool handleSpecialNumbers READ handleSpecialNumbers WRITE setHandleSpecialNumbers NOTIFY handleSpecialNumbersChanged)
+
+public:
+	//! Additional official CBOR-Tags, taken from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+	enum ExtendedTags : std::underlying_type_t<QCborTag> {
+		GenericObject = 27, //!< Serialised language-independent object with type name and constructor arguments
+		RationaleNumber = 30, //!< Rational number
+		Identifier = 39, //!< Identifier
+		Homogeneous = 41, //!< Homogeneous Array
+		Set = 258, //!< Mathematical finite set
+		ExplicitMap = 259, //!< Map datatype with key-value operations (e.g. `.get()/.set()/.delete()`)
+		NetworkAddress = 260, //!< Network Address (IPv4 or IPv6 or MAC Address)
+		NetworkAddressPrefix = 261, //!< Network Address Prefix (IPv4 or IPv6 Address + Mask Length)
+	};
+	Q_ENUM(ExtendedTags)
+
+	// Additional unofficial CBOR-Tags, as defined by this library to tag Qt types for better typesafety
+	enum CustomTags : std::underlying_type_t<QCborTag> {
+		Color = 10000, //!< Tag used for QColor
+		Font = 10001, //!< Tag used for QFont
+		Enum = 10002, //!< Tag used for enums
+		Flags = 10003, //!< Tag used for flags
+		ConstructedObject = 10004, //!< Tag used for constructed object (GenericObject + standard object)
+		Pair = 10005, //!< Tag used for QPair/std::pair
+		MultiMap = 10006, //!< Tag used for QMultiMap/QMultiHash
+		VersionNumber = 10007, //!< Tag used for QVersionNumber
+		Tuple = 10008, //!< Tag used for std::tuple
+		BitArray = 10009, //!< Tag used for QBitArray
+		Date = 10010, //!< Tag used for QDate (short ISO format)
+		Time = 10011, //!< Tag used for QTime (short ISO format)
+
+		LocaleISO = 10100, //!< Tag used for QLocale, encoded via the ISO format
+		LocaleBCP47 = 10101, //!< Tag used for QLocale, encoded via the BCP47 format
+
+		GeomSize = 10110, //!< Tag used for QSize/QSizeF
+		GeomPoint = 10111, //!< Tag used for QPoint/QPointF
+		GeomLine = 10112, //!< Tag used for QLine/QLineF
+		GeomRect = 10113, //!< Tag used for QRect/QRectF
+
+		ChronoNanoSeconds = 10120, //!< Tag used for std::chrono::nanoseconds
+		ChronoMicroSeconds = 10121, //!< Tag used for std::chrono::microseconds
+		ChronoMilliSeconds = 10122, //!< Tag used for std::chrono::milliseconds
+		ChronoSeconds = 10123, //!< Tag used for std::chrono::seconds
+		ChronoMinutes = 10124, //!< Tag used for std::chrono::minutes
+		ChronoHours = 10125, //!< Tag used for std::chrono::hours
+
+		NoTag = std::numeric_limits<std::underlying_type_t<QCborTag>>::max() //!< Used as placeholder for setTypeTag to not change the tag
+	};
+	Q_ENUM(CustomTags)
+
+	//! Default constructor
+	explicit CborSerializer(QObject *parent = nullptr);
+
+	//! @readAcFn{CborSerializer::handleSpecialNumbers}
+	bool handleSpecialNumbers() const;
+
+	//! Set a tag to always be used when serializing the given type
+	template <typename T>
+	void setTypeTag(QCborTag tag = static_cast<QCborTag>(NoTag));
+	//! @copybrief CborSerializer::setTypeTag(QCborTag)
+	void setTypeTag(int metaTypeId, QCborTag tag = static_cast<QCborTag>(NoTag));
+	//! @copybrief TypeConverter::SerializationHelper::typeTag
+	template <typename T>
+	QCborTag typeTag() const;
+	QCborTag typeTag(int metaTypeId) const override;
+
+	//! Serializers a QVariant value to a QCborValue
+	QCborValue serialize(const QVariant &data) const;
+	//! Serializers a QVariant value to a device
+	void serializeTo(QIODevice *device, const QVariant &data, QCborValue::EncodingOptions options = QCborValue::NoTransformation) const;
+	//! Serializers a QVariant value to a byte array
+	QByteArray serializeTo(const QVariant &data, QCborValue::EncodingOptions options = QCborValue::NoTransformation) const;
+
+	//! Serializers a c++ type to cbor
+	template <typename T>
+	QCborValue serialize(const T &data) const;
+	//! Serializers a c++ type to a device
+	template <typename T>
+	void serializeTo(QIODevice *device, const T &data, QCborValue::EncodingOptions options = QCborValue::NoTransformation) const;
+	//! Serializers a c++ type to a byte array
+	template <typename T>
+	QByteArray serializeTo(const T &data, QCborValue::EncodingOptions options = QCborValue::NoTransformation) const;
+
+	//! Deserializes a QCborValue to a QVariant value, based on the given type id
+	QVariant deserialize(const QCborValue &cbor, int metaTypeId, QObject *parent = nullptr) const;
+	//! Deserializes data from a device to a QVariant value, based on the given type id
+	QVariant deserializeFrom(QIODevice *device, int metaTypeId, QObject *parent = nullptr) const;
+	//! Deserializes data from a device to a QVariant value, based on the given type id
+	QVariant deserializeFrom(const QByteArray &data, int metaTypeId, QObject *parent = nullptr) const;
+
+	//! Deserializes cbor to the given c++ type
+	template <typename T>
+	T deserialize(const QCborValue &cbor, QObject *parent = nullptr) const;
+	//! Deserializes data from a device to the given c++ type
+	template <typename T>
+	T deserializeFrom(QIODevice *device, QObject *parent = nullptr) const;
+	//! Deserializes data from a byte array to the given c++ type
+	template <typename T>
+	T deserializeFrom(const QByteArray &data, QObject *parent = nullptr) const;
+
+	std::variant<QCborValue, QJsonValue> serializeGeneric(const QVariant &value) const override;
+	QVariant deserializeGeneric(const std::variant<QCborValue, QJsonValue> &value, int metaTypeId, QObject *parent) const override;
+
+public Q_SLOTS:
+	//! @writeAcFn{CborSerializer::handleSpecialNumbers}
+	void setHandleSpecialNumbers(bool handleSpecialNumbers);
+
+Q_SIGNALS:
+	//! @notifyAcFn{CborSerializer::handleSpecialNumbers}
+	void handleSpecialNumbersChanged(bool handleSpecialNumbers, QPrivateSignal);
+
+protected:
+	// protected implementation -> internal use for the type converters
+	bool jsonMode() const override;
+	QList<int> typesForTag(QCborTag tag) const override;
+
+private:
+	Q_DECLARE_PRIVATE(CborSerializer)
+};
+
+// ------------- generic implementation -------------
+
+template<typename T>
+void CborSerializer::setTypeTag(QCborTag tag)
+{
+	setTypeTag(qMetaTypeId<T>(), tag);
+}
+
+template<typename T>
+QCborTag CborSerializer::typeTag() const
+{
+	return typeTag(qMetaTypeId<T>());
+}
+
+template<typename T>
+QCborValue CborSerializer::serialize(const T &data) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be serialized");
+	return serialize(__private::variant_helper<T>::toVariant(data));
+}
+
+template<typename T>
+void CborSerializer::serializeTo(QIODevice *device, const T &data, QCborValue::EncodingOptions options) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be serialized");
+	serializeTo(device, __private::variant_helper<T>::toVariant(data), options);
+}
+
+template<typename T>
+QByteArray CborSerializer::serializeTo(const T &data, QCborValue::EncodingOptions options) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be serialized");
+	return serializeTo(__private::variant_helper<T>::toVariant(data), options);
+}
+
+template<typename T>
+T CborSerializer::deserialize(const QCborValue &cbor, QObject *parent) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be deserialized");
+	return __private::variant_helper<T>::fromVariant(deserialize(cbor, qMetaTypeId<T>(), parent));
+}
+
+template<typename T>
+T CborSerializer::deserializeFrom(QIODevice *device, QObject *parent) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be deserialized");
+	return __private::variant_helper<T>::fromVariant(deserializeFrom(device, qMetaTypeId<T>(), parent));
+}
+
+template<typename T>
+T CborSerializer::deserializeFrom(const QByteArray &data, QObject *parent) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be deserialized");
+	return __private::variant_helper<T>::fromVariant(deserializeFrom(data, qMetaTypeId<T>(), parent));
+}
+
+}
+
+#endif // QTJSONSERIALIZER_CBORSERIALIZER_H

+ 39 - 0
jsonserializer/cborserializer_p.h

@@ -0,0 +1,39 @@
+#ifndef QTJSONSERIALIZER_CBORSERIALIZER_P_H
+#define QTJSONSERIALIZER_CBORSERIALIZER_P_H
+
+#include "cborserializer.h"
+#include "serializerbase_p.h"
+
+#include <optional>
+
+#include <QtCore/QHash>
+#include <QtCore/QLoggingCategory>
+
+namespace QtJsonSerializer {
+
+class Q_JSONSERIALIZER_EXPORT CborSerializerPrivate : public SerializerBasePrivate
+{
+	Q_DECLARE_PUBLIC(CborSerializer)
+
+public:
+	using ExtendedTags = CborSerializer::ExtendedTags;
+	using CustomTags = CborSerializer::CustomTags;
+
+	mutable QReadWriteLock typeTagsLock {};
+	QHash<int, QCborTag> typeTags {};
+	bool handleSpecialNumbers = false;
+
+	QVariant deserializeCborValue(int propertyType, const QCborValue &value) const override;
+
+	QVariant deserializePositiveBignum(const QByteArray &data) const;
+	QVariant deserializeNegativeBignum(const QByteArray &data) const;
+	qreal deserializeDecimal(const QCborArray &data) const;
+	qreal deserializeBigfloat(const QCborArray &data) const;
+	qreal deserializeRationaleNumber(const QCborArray &data) const;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(logCbor)
+
+}
+
+#endif // QTJSONSERIALIZER_CBORSERIALIZER_P_H

+ 83 - 0
jsonserializer/exception.cpp

@@ -0,0 +1,83 @@
+#include "exception.h"
+#include "exception_p.h"
+#include "exceptioncontext_p.h"
+using namespace QtJsonSerializer;
+
+Exception::Exception(const QByteArray &what) :
+	d{new ExceptionPrivate{what}}
+{}
+
+const char *Exception::what() const noexcept
+{
+	return d->what.constData();
+}
+
+QByteArray Exception::message() const
+{
+	return d->message;
+}
+
+Exception::PropertyTrace Exception::propertyTrace() const
+{
+	return d->trace;
+}
+
+void Exception::raise() const
+{
+	throw *this;
+}
+
+ExceptionBase *Exception::clone() const
+{
+	auto exc = new Exception(QByteArray());
+	exc->d = d;
+	return exc;
+}
+
+SerializationException::SerializationException(const QByteArray &what) :
+	Exception{"Failed to serialize with error: " + what}
+{}
+
+void SerializationException::raise() const
+{
+	throw *this;
+}
+
+ExceptionBase *SerializationException::clone() const
+{
+	auto exc = new SerializationException(QByteArray());
+	exc->d = d;
+	return exc;
+}
+
+DeserializationException::DeserializationException(const QByteArray &what) :
+	Exception{"Failed to deserialize with error: " + what}
+{}
+
+void DeserializationException::raise() const
+{
+	throw *this;
+}
+
+ExceptionBase *DeserializationException::clone() const
+{
+	auto exc = new DeserializationException(QByteArray());
+	exc->d = d;
+	return exc;
+}
+
+
+
+ExceptionPrivate::ExceptionPrivate(QByteArray msg) :
+	message{std::move(msg)},
+	trace{ExceptionContext::currentContext()}
+{
+	//construct the whole trace
+	what = "what: " + message + "\nProperty Trace:";
+	if(trace.isEmpty())
+		what += " <root element>";
+	else {
+		for(const auto &p : qAsConst(trace))
+			what += "\n\t" + p.first + " (Type: " + p.second + ")";
+	}
+}

+ 79 - 0
jsonserializer/exception.h

@@ -0,0 +1,79 @@
+#ifndef QTJSONSERIALIZER_EXCEPTION_H
+#define QTJSONSERIALIZER_EXCEPTION_H
+
+#include "qtjsonserializer_global.h"
+
+#include <QtCore/qstring.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qstack.h>
+
+#if !defined(QT_NO_EXCEPTIONS) && QT_CONFIG(future)
+#include <QtCore/qexception.h>
+namespace QtJsonSerializer {
+//! The exception base class to use for this module
+using ExceptionBase = QException;
+}
+#else
+#include <exception>
+namespace QtJsonSerializer {
+//! The exception base class to use for this module
+using ExceptionBase = std::exception;
+}
+#endif
+
+namespace QtJsonSerializer {
+
+class ExceptionPrivate;
+//! Exception thrown by QJsonSerializer if something goes wrong
+class Q_JSONSERIALIZER_EXPORT Exception : public ExceptionBase
+{
+public:
+	//! The type of a stack of a property trace (name, type)
+	using PropertyTrace = QStack<QPair<QByteArray, QByteArray>>;
+
+	//! Constructor with error message
+	Exception(const QByteArray &what);
+
+	//! @inherit{std::exception::what}
+	const char *what() const noexcept final;
+
+	//! Returns the error message, without the property trace
+	QByteArray message() const;
+	//! Returns the property trace
+	PropertyTrace propertyTrace() const;
+
+	//! @inherit{QException::raise}
+	virtual void raise() const override;
+	//! @inherit{QException::clone}
+	virtual ExceptionBase *clone() const override;
+
+protected:
+	//! @private
+	QSharedPointer<ExceptionPrivate> d;
+};
+
+//! Exception thrown by the serializers if something goes wrong while serializing
+class Q_JSONSERIALIZER_EXPORT SerializationException : public Exception
+{
+public:
+	//! Constructor with error message
+	SerializationException(const QByteArray &what);
+
+	void raise() const override;
+	ExceptionBase *clone() const override;
+};
+
+//! Exception thrown by the serializers if something goes wrong while deserializing
+class Q_JSONSERIALIZER_EXPORT DeserializationException : public Exception
+{
+public:
+	//! Constructor with error message
+	DeserializationException(const QByteArray &what);
+
+	void raise() const override;
+	ExceptionBase *clone() const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_EXCEPTION_H

+ 22 - 0
jsonserializer/exception_p.h

@@ -0,0 +1,22 @@
+#ifndef QTJSONSERIALIZER_EXCEPTION_P_H
+#define QTJSONSERIALIZER_EXCEPTION_P_H
+
+#include "qtjsonserializer_global.h"
+#include "exception.h"
+
+namespace QtJsonSerializer {
+
+class Q_JSONSERIALIZER_EXPORT ExceptionPrivate
+{
+	Q_DISABLE_COPY(ExceptionPrivate)
+public:
+	ExceptionPrivate(QByteArray message);
+
+	QByteArray message;
+	Exception::PropertyTrace trace;
+	QByteArray what;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_EXCEPTION_P_H

+ 43 - 0
jsonserializer/exceptioncontext.cpp

@@ -0,0 +1,43 @@
+#include "exceptioncontext_p.h"
+using namespace QtJsonSerializer;
+
+Q_LOGGING_CATEGORY(QtJsonSerializer::logExceptCtx, "qt.jsonserializer.private.exceptioncontext")
+
+QThreadStorage<SerializationException::PropertyTrace> ExceptionContext::contextStore;
+
+ExceptionContext::ExceptionContext(const QMetaProperty &property)
+{
+	contextStore.localData().push({
+									  property.name(),
+									  property.isEnumType() ?
+										 property.enumerator().name() :
+										 property.typeName()
+								  });
+}
+
+ExceptionContext::ExceptionContext(int propertyType, const QByteArray &hint)
+{
+	contextStore.localData().push({
+									  hint.isNull() ? QByteArray("<unnamed>") : hint,
+									  QMetaTypeName(propertyType)
+								  });
+}
+
+ExceptionContext::~ExceptionContext()
+{
+	auto &context = contextStore.localData();
+	if (context.isEmpty())
+		qCWarning(logExceptCtx) << "Corrupted context store";
+	else
+		context.pop();
+}
+
+SerializationException::PropertyTrace ExceptionContext::currentContext()
+{
+	return contextStore.localData();
+}
+
+int ExceptionContext::currentDepth()
+{
+	return contextStore.localData().size();
+}

+ 31 - 0
jsonserializer/exceptioncontext_p.h

@@ -0,0 +1,31 @@
+#ifndef QTJSONSERIALIZER_EXCEPTIONCONTEXT_P_H
+#define QTJSONSERIALIZER_EXCEPTIONCONTEXT_P_H
+
+#include "qtjsonserializer_global.h"
+#include "exception.h"
+
+#include <QtCore/QMetaProperty>
+#include <QtCore/QThreadStorage>
+#include <QtCore/QLoggingCategory>
+
+namespace QtJsonSerializer {
+
+class Q_JSONSERIALIZER_EXPORT ExceptionContext
+{
+public:
+	ExceptionContext(const QMetaProperty &property);
+	ExceptionContext(int propertyType, const QByteArray &hint);
+	~ExceptionContext();
+
+	static SerializationException::PropertyTrace currentContext();
+	static int currentDepth();
+
+private:
+	static QThreadStorage<SerializationException::PropertyTrace> contextStore;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(logExceptCtx)
+
+}
+
+#endif // QTJSONSERIALIZER_EXCEPTIONCONTEXT_P_H

+ 143 - 0
jsonserializer/jsonserializer.cpp

@@ -0,0 +1,143 @@
+#include "jsonserializer.h"
+#include "jsonserializer_p.h"
+
+#include <QtCore/QBuffer>
+using namespace QtJsonSerializer;
+
+JsonSerializer::JsonSerializer(QObject *parent) :
+	  SerializerBase{*new JsonSerializerPrivate{}, parent}
+{}
+
+QJsonValue JsonSerializer::serialize(const QVariant &data) const
+{
+	return serializeVariant(data.userType(), data).toJsonValue();
+}
+
+void JsonSerializer::serializeTo(QIODevice *device, const QVariant &data, QJsonDocument::JsonFormat format) const
+{
+	if (!device->isOpen() || !device->isWritable())
+		throw SerializationException{"QIODevice must be open and writable!"};
+	QJsonDocument doc;
+	const auto jData = serialize(data);
+	if (jData.isArray())
+		doc = QJsonDocument{jData.toArray()};
+	else if (jData.isObject())
+		doc = QJsonDocument{jData.toObject()};
+	else
+		throw SerializationException{"Only objects or arrays can be written to a device!"};
+	device->write(doc.toJson(format));
+}
+
+QByteArray JsonSerializer::serializeTo(const QVariant &data, QJsonDocument::JsonFormat format) const
+{
+	QBuffer buffer;
+	if (!buffer.open(QIODevice::WriteOnly))
+		throw SerializationException{"Failed to write to bytearray buffer with error: " + buffer.errorString().toUtf8()};
+	serializeTo(&buffer, data, format);
+	buffer.close();
+	return buffer.data();
+}
+
+QVariant JsonSerializer::deserialize(const QJsonValue &json, int metaTypeId, QObject *parent) const
+{
+    return deserializeVariant(metaTypeId, QCborValue::fromJsonValue(json), parent);
+}
+
+QVariant JsonSerializer::deserializeFrom(QIODevice *device, int metaTypeId, QObject *parent) const
+{
+	if (!device->isOpen() || !device->isReadable())
+		throw DeserializationException{"QIODevice must be open and readable!"};
+	QJsonParseError error;
+	auto doc = QJsonDocument::fromJson(device->readAll(), &error);
+	if (error.error != QJsonParseError::NoError)
+		throw DeserializationException{"Failed to read file as JSON with error: " + error.errorString().toUtf8()};
+	if (doc.isArray())
+		return deserialize(doc.array(), metaTypeId, parent);
+	else if (doc.isObject())
+		return deserialize(doc.object(), metaTypeId, parent);
+	else if (doc.isNull())
+		return deserialize(QJsonValue::Null, metaTypeId, parent);
+	else
+		return QVariant{};
+}
+
+QVariant JsonSerializer::deserializeFrom(const QByteArray &data, int metaTypeId, QObject *parent) const
+{
+	QBuffer buffer(const_cast<QByteArray*>(&data));
+	if (!buffer.open(QIODevice::ReadOnly))
+		throw DeserializationException{"Failed to read from bytearray buffer with error: " + buffer.errorString().toUtf8()};
+	auto res = deserializeFrom(&buffer, metaTypeId, parent);
+	buffer.close();
+	return res;
+}
+
+JsonSerializer::ByteArrayFormat JsonSerializer::byteArrayFormat() const
+{
+	Q_D(const JsonSerializer);
+	return d->byteArrayFormat;
+}
+
+bool JsonSerializer::validateBase64() const
+{
+	Q_D(const JsonSerializer);
+	return d->validateBase64;
+}
+
+std::variant<QCborValue, QJsonValue> JsonSerializer::serializeGeneric(const QVariant &value) const
+{
+	return serialize(value);
+}
+
+QVariant JsonSerializer::deserializeGeneric(const std::variant<QCborValue, QJsonValue> &value, int metaTypeId, QObject *parent) const
+{
+	return deserialize(std::get<QJsonValue>(value), metaTypeId, parent);
+}
+
+void JsonSerializer::setByteArrayFormat(JsonSerializer::ByteArrayFormat byteArrayFormat)
+{
+	Q_D(JsonSerializer);
+	if(d->byteArrayFormat == byteArrayFormat)
+		return;
+
+	d->byteArrayFormat = byteArrayFormat;
+	emit byteArrayFormatChanged(d->byteArrayFormat, {});
+}
+
+void JsonSerializer::setValidateBase64(bool validateBase64)
+{
+	Q_D(JsonSerializer);
+	if(d->validateBase64 == validateBase64)
+		return;
+
+	d->validateBase64 = validateBase64;
+	emit validateBase64Changed(d->validateBase64, {});
+}
+
+bool JsonSerializer::jsonMode() const
+{
+	return true;
+}
+
+QCborTag JsonSerializer::typeTag(int metaTypeId) const
+{
+	Q_D(const JsonSerializer);
+	if (metaTypeId == QMetaType::QByteArray) {
+		switch (d->byteArrayFormat) {
+		case ByteArrayFormat::Base64:
+			return static_cast<QCborTag>(QCborKnownTags::ExpectedBase64);
+		case ByteArrayFormat::Base64url:
+			return static_cast<QCborTag>(QCborKnownTags::ExpectedBase64url);
+		case ByteArrayFormat::Base16:
+			return static_cast<QCborTag>(QCborKnownTags::ExpectedBase16);
+		default:
+			Q_UNREACHABLE();
+		}
+	} else
+		return TypeConverter::NoTag;
+}
+
+QList<int> JsonSerializer::typesForTag(QCborTag tag) const
+{
+	Q_UNUSED(tag)
+	return {};
+}

+ 147 - 0
jsonserializer/jsonserializer.h

@@ -0,0 +1,147 @@
+#ifndef QTJSONSERIALIZER_JSONSERIALIZER_H
+#define QTJSONSERIALIZER_JSONSERIALIZER_H
+
+#include "qtjsonserializer_global.h"
+
+#include "serializerbase.h"
+
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+
+namespace QtJsonSerializer {
+
+class JsonSerializerPrivate;
+//! A class to serialize and deserialize c++ classes to and from JSON
+class Q_JSONSERIALIZER_EXPORT JsonSerializer : public SerializerBase
+{
+	Q_OBJECT
+
+	//! Specifies the format, in which bytearray should be converter to a JSON string
+	Q_PROPERTY(ByteArrayFormat byteArrayFormat READ byteArrayFormat WRITE setByteArrayFormat NOTIFY byteArrayFormatChanged)
+	//! Specify whether deserializing a QByteArray should verify the data as base64 instead of silent discarding
+	Q_PROPERTY(bool validateBase64 READ validateBase64 WRITE setValidateBase64 NOTIFY validateBase64Changed)
+
+public:
+	//! Defines the different supported bytearray formats
+	enum class ByteArrayFormat {
+		Base64, //!< Data is encoded as base64 string with padding
+		Base64url, //!< Data is encoded as base64url string, without padding
+		Base16  //!< Data is encoded as hexadecimal string (any case)
+	};
+	Q_ENUM(ByteArrayFormat)
+
+	//! Default constructor
+	explicit JsonSerializer(QObject *parent = nullptr);
+
+	//! Serializers a QVariant value to a QJsonValue
+	QJsonValue serialize(const QVariant &data) const;
+	//! Serializers a QVariant value to a device
+	void serializeTo(QIODevice *device, const QVariant &data, QJsonDocument::JsonFormat format = QJsonDocument::Compact) const;
+	//! Serializers a QVariant value to a byte array
+	QByteArray serializeTo(const QVariant &data, QJsonDocument::JsonFormat format = QJsonDocument::Compact) const;
+
+	//! Serializers a generic c++ type to json
+	template <typename T>
+	typename QtJsonSerializer::__private::json_type<T>::type serialize(const T &data) const;
+	//! Serializers a generic c++ type to a device
+	template <typename T>
+	void serializeTo(QIODevice *device, const T &data, QJsonDocument::JsonFormat format = QJsonDocument::Compact) const;
+	//! Serializers a generic c++ type to a byte array
+	template <typename T>
+	QByteArray serializeTo(const T &data, QJsonDocument::JsonFormat format = QJsonDocument::Compact) const;
+
+	//! Deserializes a QJsonValue to a QVariant value, based on the given type id
+	QVariant deserialize(const QJsonValue &json, int metaTypeId, QObject *parent = nullptr) const;
+	//! Deserializes data from a device to a QVariant value, based on the given type id
+	QVariant deserializeFrom(QIODevice *device, int metaTypeId, QObject *parent = nullptr) const;
+	//! Deserializes data from a device to a QVariant value, based on the given type id
+	QVariant deserializeFrom(const QByteArray &data, int metaTypeId, QObject *parent = nullptr) const;
+
+	//! Deserializes a json to the given c++ type
+	template <typename T>
+	T deserialize(const typename QtJsonSerializer::__private::json_type<T>::type &json, QObject *parent = nullptr) const;
+	//! Deserializes data from a device to the given c++ type
+	template <typename T>
+	T deserializeFrom(QIODevice *device, QObject *parent = nullptr) const;
+	//! Deserializes data from a byte array to the given c++ type
+	template <typename T>
+	T deserializeFrom(const QByteArray &data, QObject *parent = nullptr) const;
+
+	//! @readAcFn{QJsonSerializer::byteArrayFormat}
+	ByteArrayFormat byteArrayFormat() const;
+	//! @readAcFn{QJsonSerializer::validateBase64}
+	bool validateBase64() const;
+
+	std::variant<QCborValue, QJsonValue> serializeGeneric(const QVariant &value) const override;
+	QVariant deserializeGeneric(const std::variant<QCborValue, QJsonValue> &value, int metaTypeId, QObject *parent) const override;
+
+public Q_SLOTS:
+	//! @writeAcFn{QJsonSerializer::byteArrayFormat}
+	void setByteArrayFormat(ByteArrayFormat byteArrayFormat);
+	//! @writeAcFn{QJsonSerializer::validateBase64}
+	void setValidateBase64(bool validateBase64);
+
+Q_SIGNALS:
+	//! @notifyAcFn{QJsonSerializer::byteArrayFormat}
+	void byteArrayFormatChanged(ByteArrayFormat byteArrayFormat, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::validateBase64}
+	void validateBase64Changed(bool validateBase64, QPrivateSignal);
+
+protected:
+	// protected implementation -> internal use for the type converters
+	bool jsonMode() const override;
+	QCborTag typeTag(int metaTypeId) const override;
+	QList<int> typesForTag(QCborTag tag) const override;
+
+private:
+	Q_DECLARE_PRIVATE(JsonSerializer)
+};
+
+// ------------- Generic Implementation -------------
+
+template<typename T>
+typename QtJsonSerializer::__private::json_type<T>::type JsonSerializer::serialize(const T &data) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be serialized");
+	return __private::json_type<T>::convert(serialize(__private::variant_helper<T>::toVariant(data)));
+}
+
+template<typename T>
+void JsonSerializer::serializeTo(QIODevice *device, const T &data, QJsonDocument::JsonFormat format) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be serialized");
+	serializeTo(device, __private::variant_helper<T>::toVariant(data), format);
+}
+
+template<typename T>
+QByteArray JsonSerializer::serializeTo(const T &data, QJsonDocument::JsonFormat format) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be serialized");
+	return serializeTo(__private::variant_helper<T>::toVariant(data), format);
+}
+
+template<typename T>
+T JsonSerializer::deserialize(const typename __private::json_type<T>::type &json, QObject *parent) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be deserialized");
+	return __private::variant_helper<T>::fromVariant(deserialize(json, qMetaTypeId<T>(), parent));
+}
+
+template<typename T>
+T JsonSerializer::deserializeFrom(QIODevice *device, QObject *parent) const
+{
+	static_assert(__private::is_serializable<T>::value, "T cannot be deserialized");
+	return __private::variant_helper<T>::fromVariant(deserializeFrom(device, qMetaTypeId<T>(), parent));
+}
+
+template<typename T>
+T JsonSerializer::deserializeFrom(const QByteArray &data, QObject *parent) const
+{
+	static_assert(QtJsonSerializer::__private::is_serializable<T>::value, "T cannot be deserialized");
+	return QtJsonSerializer::__private::variant_helper<T>::fromVariant(deserializeFrom(data, qMetaTypeId<T>(), parent));
+}
+
+}
+
+#endif // QTJSONSERIALIZER_JSONSERIALIZER_H

+ 92 - 0
jsonserializer/jsonserializer.pri

@@ -0,0 +1,92 @@
+# TARGET = QtJsonSerializer
+
+QT += core core-private
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+        $$PWD/cborserializer.h \
+	$$PWD/cborserializer_p.h \
+	$$PWD/exception.h \
+	$$PWD/exception_p.h \
+	$$PWD/exceptioncontext_p.h \
+	$$PWD/jsonserializer.h \
+	$$PWD/jsonserializer_p.h \
+	$$PWD/metawriters.h \
+	$$PWD/metawriters_p.h \
+	$$PWD/qtjsonserializer_global.h \
+	$$PWD/qtjsonserializer_helpertypes.h \
+	$$PWD/serializerbase.h \
+	$$PWD/serializerbase_p.h \
+	$$PWD/typeconverter.h \
+	$$PWD/typeextractors.h
+
+SOURCES += \
+        $$PWD/cborserializer.cpp \
+	$$PWD/exception.cpp \
+	$$PWD/exceptioncontext.cpp \
+	$$PWD/jsonserializer.cpp \
+	$$PWD/metawriters.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QByteArray.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QCborArray.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QCborMap.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QCborValue.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QChar.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QDate.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QDateTime.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QJsonArray.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QJsonObject.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QJsonValue.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QLine.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QLineF.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QLocale.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QMimeType.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QObject_.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QPoint.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QPointF.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QRect.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QRectF.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QRegularExpression.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QSize.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QSizeF.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QString.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QTime.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QUrl.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QUuid.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_QVersionNumber.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_bool.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_char.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_double.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_float.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_hook.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_int.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_long.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_qlonglong.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_qulonglong.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_short.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_signed_char.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_uchar.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_uint.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_ulong.cpp \
+    $$PWD/qjsonconverterreg/qjsonconverterreg_ushort.cpp \
+	$$PWD/serializerbase.cpp \
+	$$PWD/typeconverter.cpp
+
+include(typeconverters/typeconverters.pri)
+# DEFINES += NO_REGISTER_JSON_CONVERTERS
+# no_register_json_converters: DEFINES += NO_REGISTER_JSON_CONVERTERS
+# else: include(qjsonreggen.pri)
+
+# load(qt_module)
+
+# win32 {
+# 	QMAKE_TARGET_COMPANY = "Skycoder42"
+# 	QMAKE_TARGET_PRODUCT = "QtJsonSerializer"
+# 	QMAKE_TARGET_COPYRIGHT = "Felix Barz"
+# } else:mac {
+# 	QMAKE_TARGET_BUNDLE_PREFIX = "de.skycoder42."
+# }
+
+# DISTFILES += \
+# 	$$PWD/QtJsonSerializer \
+# 	typesplit.pri

+ 21 - 0
jsonserializer/jsonserializer_p.h

@@ -0,0 +1,21 @@
+#ifndef QTJSONSERIALIZER_JSONSERIALIZER_P_H
+#define QTJSONSERIALIZER_JSONSERIALIZER_P_H
+
+#include "jsonserializer.h"
+#include "serializerbase_p.h"
+
+namespace QtJsonSerializer {
+
+class Q_JSONSERIALIZER_EXPORT JsonSerializerPrivate : public SerializerBasePrivate
+{
+	Q_DECLARE_PUBLIC(JsonSerializer)
+
+public:
+	using ByteArrayFormat = JsonSerializer::ByteArrayFormat;
+	ByteArrayFormat byteArrayFormat = ByteArrayFormat::Base64;
+	bool validateBase64 = true;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_JSONSERIALIZER_P_H

+ 275 - 0
jsonserializer/metawriters.cpp

@@ -0,0 +1,275 @@
+#include "metawriters.h"
+#include "metawriters_p.h"
+
+#include <QtCore/QRegularExpression>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::MetaWriters;
+using namespace QtJsonSerializer::MetaWriters::Implementations;
+
+Q_LOGGING_CATEGORY(QtJsonSerializer::MetaWriters::logSeqWriter, "qt.jsonserializer.metawriters.sequential")
+Q_LOGGING_CATEGORY(QtJsonSerializer::MetaWriters::logAsocWriter, "qt.jsonserializer.metawriters.associative")
+
+void SequentialWriter::registerWriter(int metaTypeId, SequentialWriterFactory *factory)
+{
+	Q_ASSERT_X(factory, Q_FUNC_INFO, "factory must not be null!");
+	QWriteLocker _{&MetaWritersPrivate::sequenceLock};
+	MetaWritersPrivate::sequenceFactories.insert(metaTypeId, factory);
+	MetaWritersPrivate::sequenceInfoCache.remove(metaTypeId);
+	qCDebug(logSeqWriter) << "Added factory for type:" << QMetaTypeName(metaTypeId);
+}
+
+bool SequentialWriter::canWrite(int metaTypeId)
+{
+	QReadLocker _{&MetaWritersPrivate::sequenceLock};
+	return MetaWritersPrivate::sequenceFactories.contains(metaTypeId);
+}
+
+QSharedPointer<SequentialWriter> SequentialWriter::getWriter(QVariant &data)
+{
+	QReadLocker _{&MetaWritersPrivate::sequenceLock};
+	const auto factory = MetaWritersPrivate::sequenceFactories.value(data.userType());
+	if (factory) {
+		qCDebug(logSeqWriter) << "Found factory for data of type:" << QMetaTypeName(data.userType());
+		return factory->create(data.data());
+	} else {
+		qCWarning(logSeqWriter) << "Unable to find factory for data of type:" << QMetaTypeName(data.userType());
+		return {};
+	}
+}
+
+SequentialWriter::SequenceInfo SequentialWriter::getInfo(int metaTypeId)
+{
+	QReadLocker rLocker{&MetaWritersPrivate::sequenceLock};
+	auto it = MetaWritersPrivate::sequenceInfoCache.find(metaTypeId);
+	if (it != MetaWritersPrivate::sequenceInfoCache.end()) {
+		qCDebug(logSeqWriter) << "Found SequenceInfo for type" << QMetaTypeName(metaTypeId)
+							  << "in cache";
+		return *it;
+	} else {
+		rLocker.unlock();
+		QWriteLocker wLocker{&MetaWritersPrivate::sequenceLock};
+		const auto factory = MetaWritersPrivate::sequenceFactories.value(metaTypeId);
+		if (factory) {
+			qCDebug(logSeqWriter) << "Found factory to generate SequenceInfo for type:" << QMetaTypeName(metaTypeId);
+			it = MetaWritersPrivate::sequenceInfoCache.insert(metaTypeId, factory->create(nullptr)->info());
+		} else {
+			qCWarning(logSeqWriter) << "Unable to find SequenceInfo for type" << QMetaTypeName(metaTypeId)
+									<< "- trying to guess by parsing the types name";
+			it = MetaWritersPrivate::sequenceInfoCache.insert(metaTypeId, MetaWritersPrivate::tryParseSequenceInfo(metaTypeId));
+		}
+		return *it;
+	}
+}
+
+SequentialWriter::~SequentialWriter() = default;
+
+SequentialWriter::SequentialWriter() = default;
+
+
+
+SequentialWriterFactory::SequentialWriterFactory() = default;
+
+SequentialWriterFactory::~SequentialWriterFactory() = default;
+
+
+
+void AssociativeWriter::registerWriter(int metaTypeId, AssociativeWriterFactory *factory)
+{
+	Q_ASSERT_X(factory, Q_FUNC_INFO, "factory must not be null!");
+	QWriteLocker _{&MetaWritersPrivate::associationLock};
+	MetaWritersPrivate::associationFactories.insert(metaTypeId, factory);
+	MetaWritersPrivate::associationInfoCache.remove(metaTypeId);
+	qCDebug(logAsocWriter) << "Added factory for type:" << QMetaTypeName(metaTypeId);
+}
+
+bool AssociativeWriter::canWrite(int metaTypeId)
+{
+	QReadLocker _{&MetaWritersPrivate::associationLock};
+	return MetaWritersPrivate::associationFactories.contains(metaTypeId);
+}
+
+QSharedPointer<AssociativeWriter> AssociativeWriter::getWriter(QVariant &data)
+{
+	QReadLocker _{&MetaWritersPrivate::associationLock};
+	const auto factory = MetaWritersPrivate::associationFactories.value(data.userType());
+	if (factory) {
+		qCDebug(logAsocWriter) << "Found factory for data of type:" << QMetaTypeName(data.userType());
+		return factory->create(data.data());
+	} else {
+		qCWarning(logAsocWriter) << "Unable to find factory for data of type:" << QMetaTypeName(data.userType());
+		return {};
+	}
+}
+
+AssociativeWriter::AssociationInfo AssociativeWriter::getInfo(int metaTypeId)
+{
+	QReadLocker rLocker{&MetaWritersPrivate::associationLock};
+	auto it = MetaWritersPrivate::associationInfoCache.find(metaTypeId);
+	if (it != MetaWritersPrivate::associationInfoCache.end()) {
+		qCDebug(logAsocWriter) << "Found SequenceInfo for type" << QMetaTypeName(metaTypeId)
+							   << "in cache";
+		return *it;
+	} else {
+		rLocker.unlock();
+		QWriteLocker wLocker{&MetaWritersPrivate::associationLock};
+		const auto factory = MetaWritersPrivate::associationFactories.value(metaTypeId);
+		if (factory) {
+			qCDebug(logAsocWriter) << "Found factory to generate SequenceInfo for type:" << QMetaTypeName(metaTypeId);
+			it = MetaWritersPrivate::associationInfoCache.insert(metaTypeId, factory->create(nullptr)->info());
+		} else {
+			qCWarning(logAsocWriter) << "Unable to find SequenceInfo for type" << QMetaTypeName(metaTypeId)
+									 << "- trying to guess by parsing the types name";
+			it = MetaWritersPrivate::associationInfoCache.insert(metaTypeId, MetaWritersPrivate::tryParseAssociationInfo(metaTypeId));
+		}
+		return *it;
+	}
+}
+
+AssociativeWriter::~AssociativeWriter() = default;
+
+AssociativeWriter::AssociativeWriter() = default;
+
+
+
+AssociativeWriterFactory::AssociativeWriterFactory() = default;
+
+AssociativeWriterFactory::~AssociativeWriterFactory() = default;
+
+
+
+SequentialWriterImpl<QList, QVariant>::SequentialWriterImpl(QVariantList *data)
+	: _data{data}
+{}
+
+SequentialWriter::SequenceInfo SequentialWriterImpl<QList, QVariant>::info() const
+{
+	return {QMetaType::UnknownType, false};
+}
+
+void SequentialWriterImpl<QList, QVariant>::reserve(int size)
+{
+	_data->reserve(size);
+}
+
+void SequentialWriterImpl<QList, QVariant>::add(const QVariant &value)
+{
+	_data->append(value);
+}
+
+
+
+AssociativeWriterImpl<QMap, QString, QVariant>::AssociativeWriterImpl(QVariantMap *data)
+	: _data{data}
+{}
+
+AssociativeWriter::AssociationInfo AssociativeWriterImpl<QMap, QString, QVariant>::info() const
+{
+	return {QMetaType::QString, QMetaType::UnknownType};
+}
+
+void AssociativeWriterImpl<QMap, QString, QVariant>::add(const QVariant &key, const QVariant &value)
+{
+	_data->insert(key.toString(), value);
+}
+
+AssociativeWriterImpl<QHash, QString, QVariant>::AssociativeWriterImpl(QVariantHash *data)
+	: _data{data}
+{}
+
+AssociativeWriter::AssociationInfo AssociativeWriterImpl<QHash, QString, QVariant>::info() const
+{
+	return {QMetaType::QString, QMetaType::UnknownType};
+}
+
+void AssociativeWriterImpl<QHash, QString, QVariant>::add(const QVariant &key, const QVariant &value)
+{
+	_data->insert(key.toString(), value);
+}
+
+// ------------- private implementation -------------
+
+QReadWriteLock MetaWritersPrivate::sequenceLock;
+QHash<int, SequentialWriterFactory*> MetaWritersPrivate::sequenceFactories {
+	{QMetaType::QStringList, new SequentialWriterFactoryQStringList{}},
+	{QMetaType::QByteArrayList, new SequentialWriterFactoryQByteArrayList{}},
+	{QMetaType::QVariantList, new SequentialWriterFactoryQVariantList{}}
+};
+QHash<int, SequentialWriter::SequenceInfo> MetaWritersPrivate::sequenceInfoCache;
+
+QReadWriteLock MetaWritersPrivate::associationLock;
+QHash<int, AssociativeWriterFactory*> MetaWritersPrivate::associationFactories {
+	{QMetaType::QVariantMap, new AssociativeWriterFactoryQVariantMap{}},
+	{QMetaType::QVariantHash, new AssociativeWriterFactoryQVariantHash{}}
+};
+QHash<int, AssociativeWriter::AssociationInfo> MetaWritersPrivate::associationInfoCache;
+
+SequentialWriter::SequenceInfo MetaWritersPrivate::tryParseSequenceInfo(int metaTypeId)
+{
+	if (metaTypeId == QMetaType::QStringList)
+		return {QMetaType::QString, false};
+	else if (metaTypeId == QMetaType::QByteArrayList)
+		return {QMetaType::QByteArray, false};
+	else if (metaTypeId == QMetaType::QVariantList)
+		return {QMetaType::UnknownType, false};
+	else {
+		static const QRegularExpression listTypeRegex{
+			QStringLiteral(R"__(^((?![0-9_])(?:\w|::)*\w)\s*<\s*(.*?)\s*>$)__"),
+			QRegularExpression::UseUnicodePropertiesOption
+		};
+		auto match = listTypeRegex.match(QString::fromUtf8(QMetaTypeName(metaTypeId)));
+		if (match.hasMatch()) {
+			return {
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+				QMetaType::type(match.captured(2).toUtf8().trimmed()),
+#else
+				QMetaType::fromName(match.captured(2).toUtf8().trimmed()).id(),
+#endif
+								match.captured(1) == QStringLiteral("QSet")
+			};
+		}
+	}
+
+	return {QMetaType::UnknownType, false};
+}
+
+AssociativeWriter::AssociationInfo MetaWritersPrivate::tryParseAssociationInfo(int metaTypeId)
+{
+	if (metaTypeId == QMetaType::QVariantMap ||
+		metaTypeId == QMetaType::QVariantHash)
+		return {QMetaType::QString, QMetaType::UnknownType};
+	else {
+		static const QRegularExpression mapTypeRegex{
+			QStringLiteral(R"__(^(?![0-9_])(?:\w|::)*\w\s*<\s*(.*?)\s*>$)__"),
+			QRegularExpression::UseUnicodePropertiesOption
+		};
+		auto match = mapTypeRegex.match(QString::fromUtf8(QMetaTypeName(metaTypeId)));
+		if (match.hasMatch()) {
+			// parse match, using <> and , rules
+			const auto matchStr = match.captured(1);
+			auto bCount = 0;
+			for (auto i = 0; i < matchStr.size(); ++i) {
+				const auto &c = matchStr[i];
+				if (c == QLatin1Char('<'))
+					++bCount;
+				else if (c == QLatin1Char('>'))
+					--bCount;
+				else if (bCount == 0 && c == QLatin1Char(',')) {
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+					return {
+						QMetaType::type(matchStr.mid(0, i).trimmed().toUtf8()),
+						QMetaType::type(matchStr.mid(i + 1).trimmed().toUtf8())
+					};
+#else
+					return {
+						QMetaType::fromName(matchStr.mid(0, i).trimmed().toUtf8()).id(),
+						QMetaType::fromName(matchStr.mid(i + 1).trimmed().toUtf8()).id()
+					};
+
+#endif
+				}
+			}
+		}
+	}
+
+	return {QMetaType::UnknownType, QMetaType::UnknownType};
+}

+ 303 - 0
jsonserializer/metawriters.h

@@ -0,0 +1,303 @@
+#ifndef QTJSONSERIALIZER_METAWRITERS_H
+#define QTJSONSERIALIZER_METAWRITERS_H
+
+#include "qtjsonserializer_global.h"
+
+#include <QtCore/qmetatype.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qreadwritelock.h>
+
+#include <QtCore/qset.h>
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore/qlinkedlist.h>
+#endif
+
+namespace QtJsonSerializer::MetaWriters {
+
+class SequentialWriterFactory;
+//! The writer class for sequential containers
+class Q_JSONSERIALIZER_EXPORT SequentialWriter
+{
+	Q_DISABLE_COPY(SequentialWriter)
+
+public:
+	//! Information about a sequential container
+	struct SequenceInfo {
+		//! The containers data type
+		int type = QMetaType::UnknownType;
+		//! Specifies if the container is a finite set
+		bool isSet = false;
+	};
+
+	//! Registers a container factory for the given container and value classes
+	template <template<typename> class TContainer, typename TClass>
+	static void registerWriter();
+	//! @copybrief SequentialWriter::registerWriter()
+	static void registerWriter(int metaTypeId, SequentialWriterFactory *factory);
+	//! Checks if a writer exists for the given type
+	static bool canWrite(int metaTypeId);
+	//! Returns a writer instance for the given data, or nullptr if none found
+	static QSharedPointer<SequentialWriter> getWriter(QVariant &data);
+	//! Returns the information details of the given type
+	static SequenceInfo getInfo(int metaTypeId);
+
+	virtual ~SequentialWriter();
+	//! Return the information for the wrapped container
+	virtual SequenceInfo info() const = 0;
+	//! Reserves space for size elements in the container
+	virtual void reserve(int size) = 0;
+	//! Adds an element to the "end" of the container
+	virtual void add(const QVariant &value) = 0;
+
+protected:
+	//! @private
+	SequentialWriter();
+};
+
+//! A factory to create sequential writer instances from variant data
+class Q_JSONSERIALIZER_EXPORT SequentialWriterFactory
+{
+	Q_DISABLE_COPY(SequentialWriterFactory)
+
+public:
+	SequentialWriterFactory();
+	virtual ~SequentialWriterFactory();
+	//! Factory method to create the instance. data can be null for read-only writers
+	virtual QSharedPointer<SequentialWriter> create(void *data) const = 0;
+};
+
+
+
+class AssociativeWriterFactory;
+//! The writer class for associative containers
+class Q_JSONSERIALIZER_EXPORT AssociativeWriter
+{
+	Q_DISABLE_COPY(AssociativeWriter)
+
+public:
+	//! Information about a associative container
+	struct AssociationInfo {
+		//! The type of the associations keys
+		int keyType = QMetaType::UnknownType;
+		//! The type of the associations values
+		int valueType = QMetaType::UnknownType;
+	};
+
+	//! Registers a container factory for the given container, key and value classes
+	template <template<typename, typename> class TContainer, typename TKey, typename TValue>
+	static void registerWriter();
+	//! @copybrief AssociativeWriter::registerWriter()
+	static void registerWriter(int metaTypeId, AssociativeWriterFactory *factory);
+	//! Checks if a writer exists for the given type
+	static bool canWrite(int metaTypeId);
+	//! Returns a writer instance for the given data, or nullptr if none found
+	static QSharedPointer<AssociativeWriter> getWriter(QVariant &data);
+	//! Returns the information details of the given type
+	static AssociationInfo getInfo(int metaTypeId);
+
+	virtual ~AssociativeWriter();
+	//! Return the information for the wrapped container
+	virtual AssociationInfo info() const = 0;
+	//! Inserts the given value for the given key into the container
+	virtual void add(const QVariant &key, const QVariant &value) = 0;
+
+protected:
+	//! @private
+	AssociativeWriter();
+};
+
+//! A factory to create associative writer instances from variant data
+class Q_JSONSERIALIZER_EXPORT AssociativeWriterFactory
+{
+	Q_DISABLE_COPY(AssociativeWriterFactory)
+
+public:
+	AssociativeWriterFactory();
+	virtual ~AssociativeWriterFactory();
+	//! Factory method to create the instance. data can be null for read-only writers
+	virtual QSharedPointer<AssociativeWriter> create(void *data) const = 0;
+};
+
+// ------------- Generic Implementation classes -------------
+
+namespace Implementations {
+
+template <template<typename> class TContainer, typename TClass>
+class SequentialWriterImpl final : public SequentialWriter
+{
+public:
+	SequentialWriterImpl(TContainer<TClass> *data)
+		: _data{data}
+	{}
+
+	SequenceInfo info() const final {
+		return {qMetaTypeId<TClass>(), false};
+	}
+
+	void reserve(int size) final {
+		_data->reserve(size);
+	}
+
+	void add(const QVariant &value) final {
+		_data->append(value.template value<TClass>());
+	}
+
+private:
+	TContainer<TClass> *_data;
+};
+
+template <template<typename> class TContainer, typename TClass>
+class SequentialWriterFactoryImpl final : public SequentialWriterFactory
+{
+public:
+	QSharedPointer<SequentialWriter> create(void *data) const final {
+		return QSharedPointer<SequentialWriterImpl<TContainer, TClass>>::create(reinterpret_cast<TContainer<TClass>*>(data));
+	}
+};
+
+
+
+template <template<typename, typename> class TContainer, typename TKey, typename TValue>
+class AssociativeWriterImpl final : public AssociativeWriter
+{
+public:
+	AssociativeWriterImpl(TContainer<TKey, TValue> *data)
+		: _data{data}
+	{}
+
+	AssociationInfo info() const final {
+		return {qMetaTypeId<TKey>(), qMetaTypeId<TValue>()};
+	}
+
+	void add(const QVariant &key, const QVariant &value) final {
+		_data->insert(key.template value<TKey>(),
+					  value.template value<TValue>());
+	}
+
+private:
+	TContainer<TKey, TValue> *_data;
+};
+
+template <template<typename, typename> class TContainer, typename TKey, typename TValue>
+class AssociativeWriterFactoryImpl final : public AssociativeWriterFactory
+{
+public:
+	QSharedPointer<AssociativeWriter> create(void *data) const final {
+		return QSharedPointer<AssociativeWriterImpl<TContainer, TKey, TValue>>::create(reinterpret_cast<TContainer<TKey, TValue>*>(data));
+	}
+};
+
+// ------------- Specializations and base generic implementations -------------
+
+template <typename TClass>
+class SequentialWriterImpl<QSet, TClass> final : public SequentialWriter
+{
+public:
+	SequentialWriterImpl(QSet<TClass> *data)
+		: _data{data}
+	{}
+
+	SequenceInfo info() const final {
+		return {qMetaTypeId<TClass>(), true};
+	}
+
+	void reserve(int size) final {
+		_data->reserve(size);
+	}
+
+	void add(const QVariant &value) final {
+		_data->insert(value.template value<TClass>());
+	}
+
+private:
+	QSet<TClass> *_data;
+};
+
+#if !defined(QT_NO_LINKED_LIST) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+template <typename TClass>
+class SequentialWriterImpl<QLinkedList, TClass> final : public SequentialWriter
+{
+public:
+	SequentialWriterImpl(QLinkedList<TClass> *data)
+		: _data{data}
+	{}
+
+	SequenceInfo info() const final {
+		return {qMetaTypeId<TClass>(), false};
+	}
+
+	void reserve(int) final {}
+
+	void add(const QVariant &value) final {
+		_data->append(value.template value<TClass>());
+	}
+
+private:
+	QLinkedList<TClass> *_data;
+};
+#endif
+
+template <>
+class SequentialWriterImpl<QList, QVariant> final : public SequentialWriter
+{
+public:
+	SequentialWriterImpl(QVariantList *data);
+
+	SequenceInfo info() const final;
+	void reserve(int size) final;
+	void add(const QVariant &value) final;
+
+private:
+	QVariantList *_data;
+};
+
+
+
+template <>
+class AssociativeWriterImpl<QMap, QString, QVariant> final : public AssociativeWriter
+{
+public:
+	AssociativeWriterImpl(QVariantMap *data);
+
+	AssociationInfo info() const final;
+	void add(const QVariant &key, const QVariant &value) final;
+
+private:
+	QVariantMap *_data;
+};
+
+template <>
+class AssociativeWriterImpl<QHash, QString, QVariant> final : public AssociativeWriter
+{
+public:
+	AssociativeWriterImpl(QVariantHash *data);
+
+	AssociationInfo info() const final;
+	void add(const QVariant &key, const QVariant &value) final;
+
+private:
+	QVariantHash *_data;
+};
+
+}
+
+template<template<typename> class TContainer, typename TClass>
+void SequentialWriter::registerWriter()
+{
+	registerWriter(qMetaTypeId<TContainer<TClass>>(),
+				   new Implementations::SequentialWriterFactoryImpl<TContainer, TClass>{});
+}
+
+template<template<typename, typename> class TContainer, typename TKey, typename TValue>
+void AssociativeWriter::registerWriter()
+{
+	registerWriter(qMetaTypeId<TContainer<TKey, TValue>>(),
+				   new Implementations::AssociativeWriterFactoryImpl<TContainer, TKey, TValue>{});
+}
+
+}
+
+#endif // QTJSONSERIALIZER_METAWRITERS_H

+ 78 - 0
jsonserializer/metawriters_p.h

@@ -0,0 +1,78 @@
+#ifndef QTJSONSERIALIZER_METAWRITERS_P_H
+#define QTJSONSERIALIZER_METAWRITERS_P_H
+
+#include "metawriters.h"
+
+#include <QtCore/QLoggingCategory>
+
+namespace QtJsonSerializer::MetaWriters {
+
+class MetaWritersPrivate
+{
+public:
+	static QReadWriteLock sequenceLock;
+	static QHash<int, SequentialWriterFactory*> sequenceFactories;
+	static QHash<int, SequentialWriter::SequenceInfo> sequenceInfoCache;
+
+	static QReadWriteLock associationLock;
+	static QHash<int, AssociativeWriterFactory*> associationFactories;
+	static QHash<int, AssociativeWriter::AssociationInfo> associationInfoCache;
+
+	static SequentialWriter::SequenceInfo tryParseSequenceInfo(int metaTypeId);
+	static AssociativeWriter::AssociationInfo tryParseAssociationInfo(int metaTypeId);
+};
+
+Q_DECLARE_LOGGING_CATEGORY(logSeqWriter)
+Q_DECLARE_LOGGING_CATEGORY(logAsocWriter)
+
+
+
+namespace Implementations {
+
+class SequentialWriterFactoryQStringList : public SequentialWriterFactory
+{
+public:
+	QSharedPointer<SequentialWriter> create(void *data) const final {
+		return QSharedPointer<SequentialWriterImpl<QList, QString>>::create(reinterpret_cast<QStringList*>(data));
+	}
+};
+
+class SequentialWriterFactoryQByteArrayList : public SequentialWriterFactory
+{
+public:
+	QSharedPointer<SequentialWriter> create(void *data) const final {
+		return QSharedPointer<SequentialWriterImpl<QList, QByteArray>>::create(reinterpret_cast<QByteArrayList*>(data));
+	}
+};
+
+class SequentialWriterFactoryQVariantList : public SequentialWriterFactory
+{
+public:
+	QSharedPointer<SequentialWriter> create(void *data) const final {
+		return QSharedPointer<SequentialWriterImpl<QList, QVariant>>::create(reinterpret_cast<QVariantList*>(data));
+	}
+};
+
+
+
+class AssociativeWriterFactoryQVariantMap : public AssociativeWriterFactory
+{
+public:
+	QSharedPointer<AssociativeWriter> create(void *data) const final {
+		return QSharedPointer<AssociativeWriterImpl<QMap, QString, QVariant>>::create(reinterpret_cast<QVariantMap*>(data));
+	}
+};
+
+class AssociativeWriterFactoryQVariantHash : public AssociativeWriterFactory
+{
+public:
+	QSharedPointer<AssociativeWriter> create(void *data) const final {
+		return QSharedPointer<AssociativeWriterImpl<QHash, QString, QVariant>>::create(reinterpret_cast<QVariantHash*>(data));
+	}
+};
+
+}
+
+}
+
+#endif // QTJSONSERIALIZER_METAWRITERS_P_H

+ 14 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QByteArray.cpp

@@ -0,0 +1,14 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QByteArray_converters() {
+	SerializerBase::registerMapConverters<QString, QByteArray>();
+	SerializerBase::registerSetConverters<QByteArray>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QCborArray.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QCborArray_converters() {
+	SerializerBase::registerBasicConverters<QCborArray>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QCborMap.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QCborMap_converters() {
+	SerializerBase::registerBasicConverters<QCborMap>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QCborValue.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QCborValue_converters() {
+	SerializerBase::registerBasicConverters<QCborValue>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QChar.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QChar_converters() {
+	SerializerBase::registerBasicConverters<QChar>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QDate.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QDate_converters() {
+	SerializerBase::registerBasicConverters<QDate>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QDateTime.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QDateTime_converters() {
+	SerializerBase::registerBasicConverters<QDateTime>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QJsonArray.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QJsonArray_converters() {
+	SerializerBase::registerBasicConverters<QJsonArray>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QJsonObject.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QJsonObject_converters() {
+	SerializerBase::registerBasicConverters<QJsonObject>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QJsonValue.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QJsonValue_converters() {
+	SerializerBase::registerBasicConverters<QJsonValue>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QLine.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QLine_converters() {
+	SerializerBase::registerListConverters<QLine>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QLineF.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QLineF_converters() {
+	SerializerBase::registerListConverters<QLineF>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QLocale.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QLocale_converters() {
+	SerializerBase::registerBasicConverters<QLocale>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QMimeType.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QMimeType_converters() {
+	SerializerBase::registerBasicConverters<QMimeType>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QObject_.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QObject__converters() {
+	SerializerBase::registerBasicConverters<QObject*>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QPoint.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QPoint_converters() {
+	SerializerBase::registerListConverters<QPoint>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QPointF.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QPointF_converters() {
+	SerializerBase::registerListConverters<QPointF>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QRect.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QRect_converters() {
+	SerializerBase::registerListConverters<QRect>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QRectF.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QRectF_converters() {
+	SerializerBase::registerListConverters<QRectF>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QRegularExpression.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QRegularExpression_converters() {
+	SerializerBase::registerBasicConverters<QRegularExpression>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QSize.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QSize_converters() {
+	SerializerBase::registerListConverters<QSize>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QSizeF.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QSizeF_converters() {
+	SerializerBase::registerListConverters<QSizeF>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QString.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QString_converters() {
+	SerializerBase::registerBasicConverters<QString>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QTime.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QTime_converters() {
+	SerializerBase::registerBasicConverters<QTime>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QUrl.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QUrl_converters() {
+	SerializerBase::registerBasicConverters<QUrl>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QUuid.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QUuid_converters() {
+	SerializerBase::registerBasicConverters<QUuid>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_QVersionNumber.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_QVersionNumber_converters() {
+	SerializerBase::registerBasicConverters<QVersionNumber>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_bool.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_bool_converters() {
+	SerializerBase::registerBasicConverters<bool>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_char.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_char_converters() {
+	SerializerBase::registerBasicConverters<char>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_double.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_double_converters() {
+	SerializerBase::registerBasicConverters<double>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_float.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_float_converters() {
+	SerializerBase::registerBasicConverters<float>();
+}
+
+}

+ 99 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_hook.cpp

@@ -0,0 +1,99 @@
+#include "qtjsonserializer_global.h"
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_bool_converters();
+void register_char_converters();
+void register_signed_char_converters();
+void register_uchar_converters();
+void register_short_converters();
+void register_ushort_converters();
+void register_int_converters();
+void register_uint_converters();
+void register_long_converters();
+void register_ulong_converters();
+void register_qlonglong_converters();
+void register_qulonglong_converters();
+void register_float_converters();
+void register_double_converters();
+void register_QObject__converters();
+void register_QChar_converters();
+void register_QString_converters();
+void register_QDate_converters();
+void register_QTime_converters();
+void register_QDateTime_converters();
+void register_QUrl_converters();
+void register_QUuid_converters();
+void register_QCborValue_converters();
+void register_QCborMap_converters();
+void register_QCborArray_converters();
+void register_QJsonValue_converters();
+void register_QJsonObject_converters();
+void register_QJsonArray_converters();
+void register_QMimeType_converters();
+void register_QVersionNumber_converters();
+void register_QLocale_converters();
+void register_QRegularExpression_converters();
+void register_QSize_converters();
+void register_QPoint_converters();
+void register_QLine_converters();
+void register_QRect_converters();
+void register_QSizeF_converters();
+void register_QPointF_converters();
+void register_QLineF_converters();
+void register_QRectF_converters();
+void register_QByteArray_converters();
+
+}
+
+namespace QtJsonSerializer {
+
+void registerTypes() {
+	static bool wasCalled = false;
+	if(wasCalled)
+		return;
+	wasCalled = true;
+	QtJsonSerializer::__private::converter_hooks::register_bool_converters();
+	QtJsonSerializer::__private::converter_hooks::register_char_converters();
+	QtJsonSerializer::__private::converter_hooks::register_signed_char_converters();
+	QtJsonSerializer::__private::converter_hooks::register_uchar_converters();
+	QtJsonSerializer::__private::converter_hooks::register_short_converters();
+	QtJsonSerializer::__private::converter_hooks::register_ushort_converters();
+	QtJsonSerializer::__private::converter_hooks::register_int_converters();
+	QtJsonSerializer::__private::converter_hooks::register_uint_converters();
+	QtJsonSerializer::__private::converter_hooks::register_long_converters();
+	QtJsonSerializer::__private::converter_hooks::register_ulong_converters();
+	QtJsonSerializer::__private::converter_hooks::register_qlonglong_converters();
+	QtJsonSerializer::__private::converter_hooks::register_qulonglong_converters();
+	QtJsonSerializer::__private::converter_hooks::register_float_converters();
+	QtJsonSerializer::__private::converter_hooks::register_double_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QObject__converters();
+	QtJsonSerializer::__private::converter_hooks::register_QChar_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QString_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QDate_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QTime_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QDateTime_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QUrl_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QUuid_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QCborValue_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QCborMap_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QCborArray_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QJsonValue_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QJsonObject_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QJsonArray_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QMimeType_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QVersionNumber_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QLocale_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QRegularExpression_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QSize_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QPoint_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QLine_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QRect_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QSizeF_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QPointF_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QLineF_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QRectF_converters();
+	QtJsonSerializer::__private::converter_hooks::register_QByteArray_converters();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_int.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_int_converters() {
+	SerializerBase::registerBasicConverters<int>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_long.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_long_converters() {
+	SerializerBase::registerBasicConverters<long>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_qlonglong.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_qlonglong_converters() {
+	SerializerBase::registerBasicConverters<qlonglong>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_qulonglong.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_qulonglong_converters() {
+	SerializerBase::registerBasicConverters<qulonglong>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_short.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_short_converters() {
+	SerializerBase::registerBasicConverters<short>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_signed_char.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_signed_char_converters() {
+	SerializerBase::registerBasicConverters<signed char>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_uchar.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_uchar_converters() {
+	SerializerBase::registerBasicConverters<uchar>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_uint.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_uint_converters() {
+	SerializerBase::registerBasicConverters<uint>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_ulong.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_ulong_converters() {
+	SerializerBase::registerBasicConverters<ulong>();
+}
+
+}

+ 13 - 0
jsonserializer/qjsonconverterreg/qjsonconverterreg_ushort.cpp

@@ -0,0 +1,13 @@
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+#include <QtCore/QtCore>
+
+#define QT_JSON_SERIALIZER_NAMED(T) #T
+
+namespace QtJsonSerializer::__private::converter_hooks {
+
+void register_ushort_converters() {
+	SerializerBase::registerBasicConverters<ushort>();
+}
+
+}

+ 87 - 0
jsonserializer/qjsonreggen.pri

@@ -0,0 +1,87 @@
+DISTFILES += \
+	$$PWD/qjsonreggen.py
+
+JSON_TYPES = \
+	bool \
+	char \
+	"signed char" \
+	uchar \
+	short \
+	ushort \
+	int \
+	uint \
+	long \
+	ulong \
+	qlonglong \
+	qulonglong \
+	float \
+	double \
+	QObject* \
+	QChar \
+	QString \
+	QDate \
+	QTime \
+	QDateTime \
+	QUrl \
+	QUuid \
+	QCborValue \
+	QCborMap \
+	QCborArray \
+	QJsonValue \
+	QJsonObject \
+	QJsonArray \
+	QMimeType \
+	QVersionNumber \
+	QLocale \
+	QRegularExpression \
+	QSize \
+	QPoint \
+	QLine \
+	QRect \
+	QSizeF \
+	QPointF \
+	QLineF \
+	QRectF \
+	QByteArray
+
+QSize.modes = list
+QPoint.modes = list
+QLine.modes = list
+QRect.modes = list
+QSizeF.modes = list
+QPointF.modes = list
+QLineF.modes = list
+QRectF.modes = list
+QByteArray.modes = map set
+
+isEmpty(QT_JSONSERIALIZER_REGGEN_DIR): QT_JSONSERIALIZER_REGGEN_DIR = $$OUT_PWD/.reggen
+debug_and_release {
+	CONFIG(debug, debug|release): QT_JSONSERIALIZER_REGGEN_DIR = $$QT_JSONSERIALIZER_REGGEN_DIR/debug
+	CONFIG(release, debug|release): QT_JSONSERIALIZER_REGGEN_DIR = $$QT_JSONSERIALIZER_REGGEN_DIR/release
+}
+mkpath($$QT_JSONSERIALIZER_REGGEN_DIR)
+
+isEmpty(QT_JSONSERIALIZER_TYPESPLIT_PY) {
+	win32: QT_JSONSERIALIZER_TYPESPLIT_PY = python
+	QT_JSONSERIALIZER_TYPESPLIT_PY += $$shell_quote($$PWD/qjsonreggen.py)
+}
+
+for(type, JSON_TYPES) {
+	type_base = $$replace(type, "\\W", "_")
+	target_base = qjsonconverterreg_$${type_base}.cpp
+	target_path = $$absolute_path($$target_base, $$QT_JSONSERIALIZER_REGGEN_DIR)
+	$${target_path}.name = $$target_path
+	$${target_path}.depends = $$PWD/qjsonreggen.py $$PWD/qjsonreggen.pri
+	$${target_path}.commands = $$QT_JSONSERIALIZER_TYPESPLIT_PY $$shell_quote($$target_path) $$shell_quote($$type) $$eval($${type_base}.modes)
+	QMAKE_EXTRA_TARGETS += $$target_path
+	GENERATED_SOURCES += $$target_path
+}
+
+escaped_types =
+for(type, JSON_TYPES): escaped_types += $$shell_quote($$type)
+target_path = $$absolute_path(qjsonconverterreg_hook.cpp, $$QT_JSONSERIALIZER_REGGEN_DIR)
+$${target_path}.name = $$target_path
+$${target_path}.depends = $$PWD/qjsonreggen.py $$PWD/qjsonreggen.pri
+$${target_path}.commands = $$QT_JSONSERIALIZER_TYPESPLIT_PY super $$shell_quote($$target_path) $$escaped_types
+QMAKE_EXTRA_TARGETS += $$target_path
+GENERATED_SOURCES += $$target_path

+ 87 - 0
jsonserializer/qjsonreggen.py

@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# Syntax: qjsonreggen.py <out_path> <class_name> <modes> ...
+# Syntax: qjsonreggen.py super <out_path> <class_names> ...
+
+import sys
+import re
+from enum import Enum
+
+class Mode(Enum):
+	ALL = 0
+	LIST = 1
+	MAP = 2
+	SET = 3
+	POINTER = 4
+	POINTER_LIST = 5
+
+
+def escaped(name):
+	return re.sub(r"\W", "_", name, re.ASCII)
+
+
+def mode_fn(mode):
+	if mode == Mode.ALL:
+		return "registerBasicConverters"
+	elif mode == Mode.LIST:
+		return "registerListConverters"
+	elif mode == Mode.MAP:
+		return "registerMapConverters"
+	elif mode == Mode.SET:
+		return "registerSetConverters"
+	elif mode == Mode.POINTER:
+		return "registerPointerConverters"
+	elif mode == Mode.POINTER_LIST:
+		return "registerPointerListConverters"
+
+
+def create_hook(file_name, class_name, *modes):
+	if len(modes) == 0:
+		modes = ["all"]
+
+	with open(file_name, "w") as file:
+		file.write('#include "qtjsonserializer_global.h"\n')
+		file.write('#include "serializerbase.h"\n')
+		file.write("#include <QtCore/QtCore>\n\n")
+
+		file.write("#define QT_JSON_SERIALIZER_NAMED(T) #T\n\n")
+
+		file.write("namespace QtJsonSerializer::__private::converter_hooks {\n\n")
+
+		file.write("void register_{}_converters() {{\n".format(escaped(class_name)))
+		for mode_name in modes:
+			mode = Mode[mode_name.upper()]
+			if mode == Mode.MAP:
+				file.write("\tSerializerBase::{}<QString, {}>();\n".format(mode_fn(mode), class_name))
+			else:
+				file.write("\tSerializerBase::{}<{}>();\n".format(mode_fn(mode), class_name))
+		file.write("}\n\n")
+
+		file.write("}\n")
+
+
+def create_super_hook(file_name, *class_names):
+	with open(file_name, "w") as file:
+		file.write('#include "qtjsonserializer_global.h"\n\n')
+
+		file.write("namespace QtJsonSerializer::__private::converter_hooks {\n\n")
+		for class_name in class_names:
+			file.write("void register_{}_converters();\n".format(escaped(class_name)))
+		file.write("\n}\n\n")
+
+		file.write("namespace QtJsonSerializer {\n\n")
+		file.write("void registerTypes() {\n")
+		file.write("\tstatic bool wasCalled = false;\n")
+		file.write("\tif(wasCalled)\n")
+		file.write("\t\treturn;\n")
+		file.write("\twasCalled = true;\n")
+		for class_name in class_names:
+			file.write("\tQtJsonSerializer::__private::converter_hooks::register_{}_converters();\n".format(escaped(class_name)))
+		file.write("}\n\n")
+		file.write("}\n")
+
+
+if __name__ == "__main__":
+	if sys.argv[1] == "super":
+		create_super_hook(sys.argv[2], *sys.argv[3:])
+	else:
+		create_hook(sys.argv[1], sys.argv[2], *sys.argv[3:])

+ 32 - 0
jsonserializer/qtjsonserializer_global.h

@@ -0,0 +1,32 @@
+#ifndef QTJSONSERIALIZER_GLOBAL_H
+#define QTJSONSERIALIZER_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+// #ifndef QT_STATIC
+// #  if defined(QT_BUILD_JSONSERIALIZER_LIB)
+// #    define Q_JSONSERIALIZER_EXPORT Q_DECL_EXPORT
+// #  else
+// #    define Q_JSONSERIALIZER_EXPORT Q_DECL_IMPORT
+// #  endif
+// #else
+// #  define Q_JSONSERIALIZER_EXPORT
+// #endif
+
+#define Q_JSONSERIALIZER_EXPORT
+
+namespace QtJsonSerializer {
+
+//! Method to register all type converters for basic Qt types
+Q_JSONSERIALIZER_EXPORT void registerTypes();
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#define QMetaTypeName(X) QMetaType::typeName(X)
+#else
+#define QMetaTypeName(X) QMetaType(X).name()
+#endif
+
+}
+
+//! @file qtjsonserializer_global.h The QtJsonSerializer library header file
+#endif // QTJSONSERIALIZER_GLOBAL_H

+ 337 - 0
jsonserializer/qtjsonserializer_helpertypes.h

@@ -0,0 +1,337 @@
+#ifndef QTJSONSERIALIZER_HELPERTYPES_H
+#define QTJSONSERIALIZER_HELPERTYPES_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qvector.h>
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore/qlinkedlist.h>
+#endif
+
+#include <QtCore/qstack.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qset.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qjsonvalue.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qcborvalue.h>
+#include <QtCore/qcbormap.h>
+#include <QtCore/qcborarray.h>
+
+#include <type_traits>
+#include <tuple>
+#include <optional>
+#include <variant>
+
+namespace QtJsonSerializer::__private {
+
+template <class T, class Enable = void>
+struct gadget_helper
+{
+	static constexpr bool value = false;
+	static inline QJsonValue convert(const QJsonValue &jsonValue) {
+		return jsonValue;
+	}
+};
+
+template <class T>
+struct gadget_helper<T, typename T::QtGadgetHelper>
+{
+	static constexpr bool value = true;
+	static inline QJsonObject convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+
+
+template <typename T>
+struct is_serializable : public std::negation<std::is_pointer<T>> {};
+
+template <typename T>
+struct is_serializable<T*> : public std::disjunction<std::is_base_of<QObject, T>, gadget_helper<T>> {};
+
+template <typename T>
+struct is_serializable<QSharedPointer<T>> : public std::is_base_of<QObject, T> {};
+
+template <typename T>
+struct is_serializable<QPointer<T>> : public std::is_base_of<QObject, T> {};
+
+template <typename T>
+struct is_serializable<QList<T>> : public is_serializable<T> {};
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+template <typename T>
+struct is_serializable<QVector<T>> : public is_serializable<T> {};
+#endif
+
+
+#if !defined(QT_NO_LINKED_LIST) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+template <typename T>
+struct is_serializable<QLinkedList<T>> : public is_serializable<T> {};
+#endif
+
+template <typename T>
+struct is_serializable<QStack<T>> : public is_serializable<T> {};
+
+template <typename T>
+struct is_serializable<QQueue<T>> : public is_serializable<T> {};
+
+template <typename TKey, typename TValue>
+struct is_serializable<QMap<TKey, TValue>> : public std::conjunction<is_serializable<TKey>, is_serializable<TValue>> {};
+
+template <typename TKey, typename TValue>
+struct is_serializable<QMultiMap<TKey, TValue>> : public std::conjunction<is_serializable<TKey>, is_serializable<TValue>> {};
+
+template <typename TKey, typename TValue>
+struct is_serializable<QHash<TKey, TValue>> : public std::conjunction<is_serializable<TKey>, is_serializable<TValue>> {};
+
+template <typename TKey, typename TValue>
+struct is_serializable<QMultiHash<TKey, TValue>> : public std::conjunction<is_serializable<TKey>, is_serializable<TValue>> {};
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+template <typename T1, typename T2>
+struct is_serializable<QPair<T1, T2>> : public std::conjunction<is_serializable<T1>, is_serializable<T2>> {};
+#endif
+
+template <typename T1, typename T2>
+struct is_serializable<std::pair<T1, T2>> : public std::conjunction<is_serializable<T1>, is_serializable<T2>> {};
+
+template <typename... TArgs>
+struct is_serializable<std::tuple<TArgs...>> : public std::conjunction<is_serializable<TArgs>...> {};
+
+template <typename T>
+struct is_serializable<std::optional<T>> : public is_serializable<T> {};
+
+template <typename... TArgs>
+struct is_serializable<std::variant<TArgs...>> : public std::conjunction<is_serializable<TArgs>...> {};
+
+
+
+template <typename T>
+struct json_type_raw : public std::conditional<gadget_helper<T>::value, QJsonObject, QJsonValue> {};
+
+template <typename T>
+struct json_type : public json_type_raw<T> {
+	static_assert(is_serializable<T>::value, "Type T must be serializable to be used in a generic expression");
+
+	static inline typename json_type_raw<T>::type convert(const QJsonValue &jsonValue) {
+		return gadget_helper<T>::convert(jsonValue);
+	}
+};
+
+template <typename T>
+struct json_type<T*> {
+	static_assert(is_serializable<T*>::value, "Only QObject deriving classes or gadget pointers can be serialized as pointer");
+	using type = QJsonObject;
+	using cborType = QCborMap;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+template <typename T>
+struct json_type<QSharedPointer<T>> {
+	static_assert(is_serializable<QSharedPointer<T>>::value, "Only QObject deriving classes can be serialized as shared pointer");
+	using type = QJsonObject;
+	using cborType = QCborMap;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+template <typename T>
+struct json_type<QPointer<T>> {
+	static_assert(is_serializable<QPointer<T>>::value, "Only QObject deriving classes can be serialized as QPointer");
+	using type = QJsonObject;
+	using cborType = QCborMap;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+template <typename T>
+struct json_type<QList<T>> {
+	static_assert(is_serializable<QList<T>>::value, "The value type of a QList must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+template <typename T>
+struct json_type<QVector<T>> {
+	static_assert(is_serializable<QVector<T>>::value, "The value type of a QVector must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+#endif
+
+#if !defined(QT_NO_LINKED_LIST) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+template <typename T>
+struct json_type<QLinkedList<T>> {
+	static_assert(is_serializable<QLinkedList<T>>::value, "The value type of a QLinkedList must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+#endif
+
+template <typename T>
+struct json_type<QStack<T>> {
+	static_assert(is_serializable<QStack<T>>::value, "The value type of a QStack must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+
+template <typename T>
+struct json_type<QQueue<T>> {
+	static_assert(is_serializable<QQueue<T>>::value, "The value type of a QQueue must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+
+template <typename TKey, typename TValue>
+struct json_type<QMap<TKey, TValue>> {
+	static_assert(is_serializable<QMap<TKey, TValue>>::value, "The key and value types of a QMap must be serializable for it to also be serializable");
+	using type = QJsonObject;
+	using cborType = QCborMap;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+template <typename TKey, typename TValue>
+struct json_type<QMultiMap<TKey, TValue>> {
+	static_assert(is_serializable<QMultiMap<TKey, TValue>>::value, "The key and value types of a QMultiMap must be serializable for it to also be serializable");
+	using type = QJsonObject;
+	using cborType = QCborMap;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+template <typename TKey, typename TValue>
+struct json_type<QHash<TKey, TValue>> {
+	static_assert(is_serializable<QHash<TKey, TValue>>::value, "The key and value types of a QHash must be serializable for it to also be serializable");
+	using type = QJsonObject;
+	using cborType = QCborMap;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+template <typename TKey, typename TValue>
+struct json_type<QMultiHash<TKey, TValue>> {
+	static_assert(is_serializable<QMultiHash<TKey, TValue>>::value, "The key and value types of a QMultiHash must be serializable for it to also be serializable");
+	using type = QJsonObject;
+	using cborType = QCborMap;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toObject();
+	}
+};
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+template <typename T1, typename T2>
+struct json_type<QPair<T1, T2>> {
+	static_assert(is_serializable<QPair<T1, T2>>::value, "All elements of a QPair must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+#endif
+
+template <typename T1, typename T2>
+struct json_type<std::pair<T1, T2>> {
+	static_assert(is_serializable<std::pair<T1, T2>>::value, "All elements of a std::pair must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+
+
+template <typename... TArgs>
+struct json_type<std::tuple<TArgs...>> {
+	static_assert(is_serializable<std::tuple<TArgs...>>::value, "All elements of a std::tuple must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+
+template <typename... TArgs>
+struct json_type<std::variant<TArgs...>> {
+	static_assert(is_serializable<std::variant<TArgs...>>::value, "All elements of a std::variant must be serializable for it to also be serializable");
+	using type = QJsonArray;
+	using cborType = QCborArray;
+
+	static inline type convert(const QJsonValue &jsonValue) {
+		return jsonValue.toArray();
+	}
+};
+
+
+
+template <typename T>
+struct variant_helper {
+	static inline QVariant toVariant(const T &data) {
+		return QVariant::fromValue<T>(data);
+	}
+	static inline T fromVariant(const QVariant &data) {
+		return data.template value<T>();
+	}
+};
+
+template <>
+struct variant_helper<QVariant> {
+	static inline QVariant toVariant(const QVariant &data) {
+		return data;
+	}
+	static inline QVariant fromVariant(const QVariant &data) {
+		return data;
+	}
+};
+
+}
+
+#endif // QTJSONSERIALIZER_HELPERTYPES_H

+ 9 - 0
jsonserializer/qtjsonserializerversion.h

@@ -0,0 +1,9 @@
+/* This file was generated by syncqt. */
+#ifndef QT_QTJSONSERIALIZER_VERSION_H
+#define QT_QTJSONSERIALIZER_VERSION_H
+
+#define QTJSONSERIALIZER_VERSION_STR "4.0.3"
+
+#define QTJSONSERIALIZER_VERSION 0x040003
+
+#endif // QT_QTJSONSERIALIZER_VERSION_H

+ 790 - 0
jsonserializer/serializerbase.cpp

@@ -0,0 +1,790 @@
+#include "serializerbase.h"
+#include "serializerbase_p.h"
+#include "exceptioncontext_p.h"
+
+#include <optional>
+#include <variant>
+#include <cmath>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QScopeGuard>
+
+#include "typeconverters/bitarrayconverter_p.h"
+#include "typeconverters/bytearrayconverter_p.h"
+#include "typeconverters/cborconverter_p.h"
+#include "typeconverters/datetimeconverter_p.h"
+#include "typeconverters/enumconverter_p.h"
+#include "typeconverters/gadgetconverter_p.h"
+#include "typeconverters/geomconverter_p.h"
+#include "typeconverters/legacygeomconverter_p.h"
+#include "typeconverters/listconverter_p.h"
+#include "typeconverters/localeconverter_p.h"
+#include "typeconverters/mapconverter_p.h"
+#include "typeconverters/multimapconverter_p.h"
+#include "typeconverters/objectconverter_p.h"
+#include "typeconverters/pairconverter_p.h"
+#include "typeconverters/smartpointerconverter_p.h"
+#include "typeconverters/stdchronodurationconverter_p.h"
+#include "typeconverters/stdoptionalconverter_p.h"
+#include "typeconverters/stdtupleconverter_p.h"
+#include "typeconverters/stdvariantconverter_p.h"
+#include "typeconverters/versionnumberconverter_p.h"
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+#ifndef NO_REGISTER_JSON_CONVERTERS
+namespace {
+void qtJsonSerializerRegisterTypes() {
+	QtJsonSerializer::registerTypes();
+}
+}
+Q_COREAPP_STARTUP_FUNCTION(qtJsonSerializerRegisterTypes);
+#endif
+
+Q_LOGGING_CATEGORY(QtJsonSerializer::logSerializer, "qt.jsonserializer.serializer")
+Q_LOGGING_CATEGORY(QtJsonSerializer::logSerializerExtractor, "qt.jsonserializer.serializer.extractor")
+
+namespace {
+
+class LogTag {
+public:
+	inline LogTag(QCborTag tag) : tag{tag} {}
+	inline operator QCborTag() const { return tag; }
+private:
+	QCborTag tag;
+};
+
+QDebug operator<<(QDebug debug, LogTag tag) {
+	if (tag != TypeConverter::NoTag)
+		debug << ", CBOR-Tag" << static_cast<QCborTag>(tag);
+	return debug;
+}
+
+}
+
+SerializerBase::SerializerBase(QObject *parent) :
+	SerializerBase{*new SerializerBasePrivate{}, parent}
+{}
+
+SerializerBase::SerializerBase(SerializerBasePrivate &dd, QObject *parent) :
+	QObject{dd, parent}
+{}
+
+void SerializerBase::registerExtractor(int metaTypeId, const QSharedPointer<TypeExtractor> &extractor)
+{
+	SerializerBasePrivate::extractors.add(metaTypeId, extractor);
+	qCDebug(logSerializerExtractor) << "Added extractor for type:" << QMetaTypeName(metaTypeId);
+}
+
+bool SerializerBase::allowDefaultNull() const
+{
+	Q_D(const SerializerBase);
+	return d->allowNull;
+}
+
+bool SerializerBase::keepObjectName() const
+{
+	Q_D(const SerializerBase);
+	return d->keepObjectName;
+}
+
+bool SerializerBase::enumAsString() const
+{
+	Q_D(const SerializerBase);
+	return d->enumAsString;
+}
+
+bool SerializerBase::versionAsString() const
+{
+	Q_D(const SerializerBase);
+	return d->versionAsString;
+}
+
+bool SerializerBase::dateAsTimeStamp() const
+{
+	Q_D(const SerializerBase);
+	return d->dateAsTimeStamp;
+}
+
+bool SerializerBase::useBcp47Locale() const
+{
+	Q_D(const SerializerBase);
+	return d->useBcp47Locale;
+}
+
+SerializerBase::ValidationFlags SerializerBase::validationFlags() const
+{
+	Q_D(const SerializerBase);
+	return d->validationFlags;
+}
+
+SerializerBase::Polymorphing SerializerBase::polymorphing() const
+{
+	Q_D(const SerializerBase);
+	return d->polymorphing;
+}
+
+SerializerBase::MultiMapMode SerializerBase::multiMapMode() const
+{
+	Q_D(const SerializerBase);
+	return d->multiMapMode;
+}
+
+bool SerializerBase::ignoresStoredAttribute() const
+{
+	Q_D(const SerializerBase);
+	return d->ignoreStoredAttribute;
+}
+
+void SerializerBase::addJsonTypeConverterFactory(TypeConverterFactory *factory)
+{
+	QWriteLocker _{&SerializerBasePrivate::typeConverterFactoryLock};
+	SerializerBasePrivate::typeConverterFactories.append(factory);
+	qCDebug(logSerializer) << "Added new global converter factory:" << factory;
+}
+
+void SerializerBase::addJsonTypeConverter(const QSharedPointer<TypeConverter> &converter)
+{
+	Q_D(SerializerBase);
+	Q_ASSERT_X(converter, Q_FUNC_INFO, "converter must not be null!");
+	converter->setHelper(this);
+	d->typeConverters.insertSorted(converter);
+	d->serCache.clear();
+	d->deserCache.clear();
+	qCDebug(logSerializer) << "Added new local converter:" << converter->name();
+}
+
+void SerializerBase::setAllowDefaultNull(bool allowDefaultNull)
+{
+	Q_D(SerializerBase);
+	if(d->allowNull == allowDefaultNull)
+		return;
+
+	d->allowNull = allowDefaultNull;
+	emit allowDefaultNullChanged(d->allowNull, {});
+}
+
+void SerializerBase::setKeepObjectName(bool keepObjectName)
+{
+	Q_D(SerializerBase);
+	if(d->keepObjectName == keepObjectName)
+		return;
+
+	d->keepObjectName = keepObjectName;
+	emit keepObjectNameChanged(d->keepObjectName, {});
+}
+
+void SerializerBase::setEnumAsString(bool enumAsString)
+{
+	Q_D(SerializerBase);
+	if(d->enumAsString == enumAsString)
+		return;
+
+	d->enumAsString = enumAsString;
+	emit enumAsStringChanged(d->enumAsString, {});
+}
+
+void SerializerBase::setVersionAsString(bool versionAsString)
+{
+	Q_D(SerializerBase);
+	if(d->versionAsString == versionAsString)
+		return;
+
+	d->versionAsString = versionAsString;
+	emit versionAsStringChanged(d->versionAsString, {});
+}
+
+void SerializerBase::setDateAsTimeStamp(bool dateAsTimeStamp)
+{
+	Q_D(SerializerBase);
+	if(d->dateAsTimeStamp == dateAsTimeStamp)
+		return;
+
+	d->dateAsTimeStamp = dateAsTimeStamp;
+	emit dateAsTimeStampChanged(d->dateAsTimeStamp, {});
+}
+
+void SerializerBase::setUseBcp47Locale(bool useBcp47Locale)
+{
+	Q_D(SerializerBase);
+	if(d->useBcp47Locale == useBcp47Locale)
+		return;
+
+	d->useBcp47Locale = useBcp47Locale;
+	emit useBcp47LocaleChanged(d->useBcp47Locale, {});
+}
+
+void SerializerBase::setValidationFlags(ValidationFlags validationFlags)
+{
+	Q_D(SerializerBase);
+	if(d->validationFlags == validationFlags)
+		return;
+
+	d->validationFlags = validationFlags;
+	emit validationFlagsChanged(d->validationFlags, {});
+}
+
+void SerializerBase::setPolymorphing(SerializerBase::Polymorphing polymorphing)
+{
+	Q_D(SerializerBase);
+	if(d->polymorphing == polymorphing)
+		return;
+
+	d->polymorphing = polymorphing;
+	emit polymorphingChanged(d->polymorphing, {});
+}
+
+void SerializerBase::setMultiMapMode(SerializerBase::MultiMapMode multiMapMode)
+{
+	Q_D(SerializerBase);
+	if(d->multiMapMode == multiMapMode)
+		return;
+
+	d->multiMapMode = multiMapMode;
+	emit multiMapModeChanged(d->multiMapMode, {});
+}
+
+void SerializerBase::setIgnoreStoredAttribute(bool ignoreStoredAttribute)
+{
+	Q_D(SerializerBase);
+	if(d->ignoreStoredAttribute == ignoreStoredAttribute)
+		return;
+
+	d->ignoreStoredAttribute = ignoreStoredAttribute;
+	emit ignoreStoredAttributeChanged(d->ignoreStoredAttribute, {});
+}
+
+QVariant SerializerBase::getProperty(const char *name) const
+{
+	return property(name);
+}
+
+QSharedPointer<const TypeExtractor> SerializerBase::extractor(int metaTypeId) const
+{
+	const auto extractor = SerializerBasePrivate::extractors.get(metaTypeId);
+	if (extractor)
+		qCDebug(logSerializerExtractor) << "Found extractor for type:" << QMetaTypeName(metaTypeId);
+	else
+		qCDebug(logSerializerExtractor) << "Unable to find extractor for type:" << QMetaTypeName(metaTypeId);
+	return extractor;
+}
+
+QCborValue SerializerBase::serializeSubtype(const QMetaProperty &property, const QVariant &value) const
+{
+	Q_D(const SerializerBase);
+	ExceptionContext ctx(property);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	if (property.isEnumType()) {
+		const auto enumId = d->getEnumId(property.enumerator(), true);
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Serializing subtype property" << property.name()
+							   << "of enum type" << QMetaTypeName(enumId);
+		return serializeVariant(enumId, value);
+	} else {
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Serializing subtype property" << property.name()
+							   << "of type" << QMetaTypeName(property.userType());
+		return serializeVariant(property.userType(), value);
+	}
+}
+
+QCborValue SerializerBase::serializeSubtype(int propertyType, const QVariant &value, const QByteArray &traceHint) const
+{
+	ExceptionContext ctx(propertyType, traceHint);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+						   << "Serializing subtype property" << traceHint
+						   << "of type" << QMetaTypeName(propertyType);
+	return serializeVariant(propertyType, value);
+}
+
+QVariant SerializerBase::deserializeSubtype(const QMetaProperty &property, const QCborValue &value, QObject *parent) const
+{
+	Q_D(const SerializerBase);
+	ExceptionContext ctx(property);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	if (property.isEnumType()) {
+		const auto enumId = d->getEnumId(property.enumerator(), false);
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Deserializing subtype property" << property.name()
+							   << "of enum type" << QMetaTypeName(enumId);
+		return deserializeVariant(enumId, value, parent, true);
+	} else {
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Deserializing subtype property" << property.name()
+							   << "of type" << QMetaTypeName(property.userType());
+		return deserializeVariant(property.userType(), value, parent);
+	}
+}
+
+QVariant SerializerBase::deserializeSubtype(int propertyType, const QCborValue &value, QObject *parent, const QByteArray &traceHint) const
+{
+	ExceptionContext ctx(propertyType, traceHint);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+						   << "Deserializing subtype property" << traceHint
+						   << "of type" << QMetaTypeName(propertyType);
+	return deserializeVariant(propertyType, value, parent);
+}
+
+QCborValue SerializerBase::serializeVariant(int propertyType, const QVariant &value) const
+{
+	Q_D(const SerializerBase);
+	// first: find a converter and convert to cbor
+	auto converter = d->findSerConverter(propertyType);
+	QCborValue res;
+	if (converter)
+		res = converter->serialize(propertyType, value);
+	else
+		res = d->serializeValue(propertyType, value);
+
+	// second: check if an override tag is given, and if yes, override the normal tag
+	if (const auto mTag = typeTag(propertyType); mTag != TypeConverter::NoTag)
+		return {mTag, res.isTag() ? res.taggedValue() : res};
+	else
+		return res;
+}
+
+QVariant SerializerBase::deserializeVariant(int propertyType, const QCborValue &value, QObject *parent, bool skipConversion) const
+{
+	Q_D(const SerializerBase);
+	// first: find a converter and convert the data to QVariant
+	auto converter = d->findDeserConverter(propertyType,
+										   value.isTag() ? value.tag() : TypeConverter::NoTag,
+										   value.isTag() ? value.taggedValue().type() : value.type());
+
+	QVariant variant;
+	if (converter) {
+		if (jsonMode())
+			variant = converter->deserializeJson(propertyType, value, parent);
+		else
+			variant = converter->deserializeCbor(propertyType, value, parent);
+	} else {
+		if (jsonMode())
+			variant = d->deserializeJsonValue(propertyType, value);
+		else
+			variant = d->deserializeCborValue(propertyType, value);
+	}
+
+	// second: if the type was given, enforce a conversion to that type (expect if skipped)
+	if(!skipConversion && propertyType != QMetaType::UnknownType) {
+		auto vType = variant.typeName();
+
+		// exclude special values that can convert from null, but should not do so
+		auto allowConvert = true;
+		switch (propertyType) {
+		case QMetaType::QString:
+		case QMetaType::QByteArray:
+			if (value.isNull())
+				allowConvert = false;
+			break;
+		default:
+			break;
+		}
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+		if(allowConvert && variant.canConvert(propertyType) && variant.convert(propertyType))
+#else
+		if(allowConvert && variant.canConvert(QMetaType(propertyType)) && variant.convert(QMetaType(propertyType)))
+#endif
+			return variant;
+        else if (d->allowNull && value.isNull())
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+			return QVariant{propertyType, nullptr};
+#else
+			return QVariant{QMetaType(propertyType), nullptr};
+#endif
+        else {
+            qDebug() << variant << value << value.isNull() << d->allowNull;
+            throw DeserializationException(
+                QByteArray("Failed to convert deserialized variant of type ")
+                + (vType ? vType : "<unknown>") + QByteArray(" to property type ")
+                + QMetaTypeName(propertyType)
+                + QByteArray(". Make shure to register converters with the "
+                             "QJsonSerializer::register* methods"));
+        }
+    } else
+        return variant;
+}
+
+// ------------- private implementation -------------
+
+SerializerBasePrivate::ThreadSafeStore<TypeExtractor> SerializerBasePrivate::extractors;
+QReadWriteLock SerializerBasePrivate::typeConverterFactoryLock;
+QList<TypeConverterFactory*> SerializerBasePrivate::typeConverterFactories {
+	new TypeConverterStandardFactory<BitArrayConverter>{},
+	new TypeConverterStandardFactory<BytearrayConverter>{},
+	new TypeConverterStandardFactory<CborConverter>{},
+	new TypeConverterStandardFactory<DateTimeConverter>{},
+	new TypeConverterStandardFactory<EnumConverter>{},
+	new TypeConverterStandardFactory<GadgetConverter>{},
+	new TypeConverterStandardFactory<GeomConverter>{},
+	new TypeConverterStandardFactory<ListConverter>{},
+	new TypeConverterStandardFactory<LocaleConverter>{},
+	new TypeConverterStandardFactory<MapConverter>{},
+	new TypeConverterStandardFactory<MultiMapConverter>{},
+	new TypeConverterStandardFactory<ObjectConverter>{},
+	new TypeConverterStandardFactory<PairConverter>{},
+	new TypeConverterStandardFactory<SmartPointerConverter>{},
+	new TypeConverterStandardFactory<StdChronoDurationConverter>{},
+	new TypeConverterStandardFactory<StdOptionalConverter>{},
+	new TypeConverterStandardFactory<StdTupleConverter>{},
+	new TypeConverterStandardFactory<StdVariantConverter>{},
+	new TypeConverterStandardFactory<VersionNumberConverter>{},
+
+	new TypeConverterStandardFactory<LegacyGeomConverter>{}
+};
+
+QSharedPointer<TypeConverter> SerializerBasePrivate::findSerConverter(int propertyType) const
+{
+	// first: update converters from factories
+	updateConverterStore();
+
+	// second: check if already cached
+	if (auto converter = serCache.get(propertyType); converter) {
+		qCDebug(logSerializer) << "Found cached serialization converter" << converter->name()
+							   << "for type:" <<  QMetaTypeName(propertyType);
+		return converter;
+	}
+
+	// third: check if the list of explicit converters has a matching one
+	QReadLocker cLocker{&typeConverters.lock};
+	for (const auto &converter : qAsConst(typeConverters.store)) {
+		if (converter && converter->canConvert(propertyType)) {
+			qCDebug(logSerializer) << "Found and cached serialization converter" << converter->name()
+								   << "for type:" <<  QMetaTypeName(propertyType);
+			// add converter to cache and return it
+			serCache.add(propertyType, converter);
+			return converter;
+		}
+	}
+
+	// fourth: no converter found: return default converter
+	qCDebug(logSerializer) << "Unable to find serialization converte for type:" <<  QMetaTypeName(propertyType)
+						   << "- falling back to default QVariant to CBOR conversion";
+	return nullptr;
+}
+
+QSharedPointer<TypeConverter> SerializerBasePrivate::findDeserConverter(int &propertyType, QCborTag tag, QCborValue::Type type) const
+{
+	Q_Q(const SerializerBase);
+	// first: update converters from factories
+	updateConverterStore();
+
+	// second: if no property type is given, try out any types associated with the tag
+	if (propertyType == QMetaType::UnknownType && tag != TypeConverter::NoTag) {
+		const auto tList = q->typesForTag(tag);
+		for (auto typeId : tList) {
+			// if any of those types has a working converter, just use that one
+			auto res = findDeserConverter(typeId, tag, type);
+			if (res) {
+				propertyType = typeId;
+				return res;
+			}
+		}
+	}
+
+	// third: check if already cached
+	if (auto converter = deserCache.get(propertyType);
+		converter && converter->canDeserialize(propertyType, tag, type) > 0) {
+		qCDebug(logSerializer) << "Found cached deserialization converter" << converter->name()
+							   << "for type" <<  QMetaTypeName(propertyType)
+							   << LogTag{tag}
+							   << "and CBOR-type" << type;
+		return converter;
+	}
+
+	// fourth: check if the list of explicit converters has a matching one
+	QReadLocker cLocker{&typeConverters.lock};
+	auto throwWrongTag = false;
+	std::optional<std::pair<QSharedPointer<TypeConverter>, int>> guessConverter;
+	for (const auto &converter : qAsConst(typeConverters.store)) {
+		if (converter) {
+			auto testType = propertyType;
+			switch (converter->canDeserialize(testType, tag, type)) {
+			case TypeConverter::Negative:
+				continue;
+			case TypeConverter::WrongTag:
+				throwWrongTag = true;
+				continue;
+			case TypeConverter::Guessed:
+				if (!guessConverter)
+					guessConverter = std::make_pair(converter, testType);
+				continue;
+			case TypeConverter::Positive:
+				break;
+			}
+
+			// add converter to cache (only happens for positive cases)
+			deserCache.add(propertyType, converter);
+			qCDebug(logSerializer) << "Found and cached deserialization converter" << converter->name()
+								   << "for type" <<  QMetaTypeName(propertyType)
+								   << LogTag{tag}
+								   << "and CBOR-type" << type;
+			return converter;
+		}
+	}
+	cLocker.unlock();
+
+	// fifth: if a guessed converter is available, use that one
+	if (guessConverter) {
+		// extract converter from info;
+		auto &[converter, newType] = *guessConverter;
+		// if valid, add to cache, set the type and return
+		if (converter) {
+			// add converter to list and cache
+			propertyType = newType;
+			deserCache.add(propertyType, converter);
+			qCDebug(logSerializer) << "Found and cached deserialization converter" << converter->name()
+								   << "by guessing the data with CBOR-tag" << tag
+								   << "and CBOR-type" << type
+								   << "is of type" << QMetaTypeName(propertyType);
+			return converter;
+		}
+	}
+
+	// sixth: if a wrong tag mark was set, throw an expection
+	if (throwWrongTag) {
+		throw DeserializationException{QByteArray{"Found converter able to handle data of type "} +
+									   QMetaTypeName(propertyType) +
+									   ", but the given CBOR tag " +
+									   QByteArray::number(static_cast<quint64>(tag)) +
+									   " is not convertible to that type."};
+	}
+
+	// seventh: no converter found: return default converter
+	qCDebug(logSerializer) << "Unable to find deserialization converte for type" <<  QMetaTypeName(propertyType)
+						   << LogTag{tag}
+						   << "and CBOR-type" << type
+						   << "- falling back to default CBOR to QVariant conversion";
+	return nullptr;
+}
+
+void SerializerBasePrivate::updateConverterStore() const
+{
+	Q_Q(const SerializerBase);
+	QReadLocker fLocker{&typeConverterFactoryLock};
+	if (typeConverterFactories.size() > typeConverters.factoryOffset.loadAcquire()) {
+		QWriteLocker cLocker{&typeConverters.lock};
+		auto max = typeConverterFactories.size();
+		for (auto i = typeConverters.factoryOffset.loadAcquire(); i < max; ++i) {
+			auto converter = typeConverterFactories[i]->createConverter();
+			if (converter) {
+				converter->setHelper(q);
+				typeConverters.insertSorted(converter, cLocker);
+				serCache.clear();
+				deserCache.clear();
+				qCDebug(logSerializer) << "Found and added new global converter:" << converter->name();
+			}
+		}
+		typeConverters.factoryOffset.storeRelease(typeConverterFactories.size());
+	}
+}
+
+int SerializerBasePrivate::getEnumId(QMetaEnum metaEnum, bool ser) const
+{
+	QByteArray eName = metaEnum.name();
+	if (const QByteArray scope = metaEnum.scope(); !scope.isEmpty())
+		eName = scope + "::" + eName;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	const auto eTypeId = QMetaType::type(eName);
+#else
+	const auto eTypeId = QMetaType::fromName(eName).id();
+#endif
+	if (eTypeId == QMetaType::UnknownType) {
+		if (ser)
+			throw SerializationException{"Unable to determine typeid of meta enum " + eName};
+		else
+			throw DeserializationException{"Unable to determine typeid of meta enum " + eName};
+	} else
+		return eTypeId;
+}
+
+QCborValue SerializerBasePrivate::serializeValue(int propertyType, const QVariant &value) const
+{
+	Q_UNUSED(propertyType)
+	return QCborValue::fromVariant(value);
+}
+
+QVariant SerializerBasePrivate::deserializeCborValue(int propertyType, const QCborValue &value) const
+{
+	Q_Q(const SerializerBase);
+	// perform strict validations
+	if (validationFlags.testFlag(ValidationFlag::StrictBasicTypes)) {
+		auto doThrow = false;
+
+		QList<QCborTag> expectedTags;
+		const auto testValue = value.isTag() ? value.taggedValue() : value;
+		switch (propertyType) {
+		case QMetaType::Bool:
+			if (!testValue.isBool())
+				doThrow = true;
+			break;
+		case QMetaType::Int:
+		case QMetaType::UInt:
+		case QMetaType::Long:
+		case QMetaType::LongLong:
+		case QMetaType::Short:
+		case QMetaType::ULong:
+		case QMetaType::ULongLong:
+		case QMetaType::UShort:
+		case QMetaType::SChar:
+		case QMetaType::UChar:
+			if (!testValue.isInteger())
+				doThrow = true;
+			break;
+		case QMetaType::Float:
+		case QMetaType::Double:
+			if (!testValue.isDouble())
+				doThrow = true;
+			break;
+		case QMetaType::Char:
+		case QMetaType::QChar:
+		case QMetaType::QString:
+			expectedTags = {
+				TypeConverter::NoTag,
+				static_cast<QCborTag>(QCborKnownTags::Base64),
+				static_cast<QCborTag>(QCborKnownTags::Base64url)
+			};
+			Q_FALLTHROUGH();
+		case QMetaType::QColor:
+		case QMetaType::QFont:
+			if (!testValue.isString())
+				doThrow = true;
+			break;
+		case QMetaType::QByteArray:
+			expectedTags = {
+				TypeConverter::NoTag,
+				static_cast<QCborTag>(QCborKnownTags::ExpectedBase64),
+				static_cast<QCborTag>(QCborKnownTags::ExpectedBase64url),
+				static_cast<QCborTag>(QCborKnownTags::ExpectedBase16)
+			};
+			if (!testValue.isByteArray())
+				doThrow = true;
+			break;
+		case QMetaType::Nullptr:
+			if (!testValue.isNull())
+				doThrow = true;
+			break;
+		case QMetaType::QUrl:
+			if (!value.isUrl()) {
+				if (testValue.isString())
+					expectedTags = {static_cast<QCborTag>(QCborKnownTags::Url)};
+				else
+					doThrow = true;
+			}
+			break;
+		case QMetaType::QUuid:
+			if (!value.isUuid()) {
+				if (testValue.isByteArray())
+					expectedTags = {static_cast<QCborTag>(QCborKnownTags::Uuid)};
+				else
+					doThrow = true;
+			}
+			break;
+		case QMetaType::QRegularExpression:
+			if (!value.isRegularExpression()) {
+				if (testValue.isString())
+					expectedTags = {static_cast<QCborTag>(QCborKnownTags::RegularExpression)};
+				else
+					doThrow = true;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (const auto mTag = q->typeTag(propertyType);
+			mTag != TypeConverter::NoTag &&
+			mTag != value.tag())
+			doThrow = true;
+		else if (!expectedTags.isEmpty() &&
+				 !expectedTags.contains(value.tag()))
+			doThrow = true;
+
+		if (doThrow) {
+			throw DeserializationException(QByteArray("Failed to deserialze CBOR-value to type ") +
+										   QMetaTypeName(propertyType) +
+										   QByteArray(" because the given CBOR-value failed strict validation"));
+		}
+	}
+
+	return value.toVariant();
+}
+
+QVariant SerializerBasePrivate::deserializeJsonValue(int propertyType, const QCborValue &value) const
+{
+	// perform strict validations
+	if (validationFlags.testFlag(ValidationFlag::StrictBasicTypes)) {
+		auto doThrow = false;
+		switch (propertyType) {
+		case QMetaType::Bool:
+			if (!value.isBool())
+				doThrow = true;
+			break;
+		case QMetaType::Int:
+		case QMetaType::UInt:
+		case QMetaType::Long:
+		case QMetaType::LongLong:
+		case QMetaType::Short:
+		case QMetaType::ULong:
+		case QMetaType::ULongLong:
+		case QMetaType::UShort:
+		case QMetaType::SChar:
+		case QMetaType::UChar:
+			if (value.isDouble()) {
+				if (auto val = value.toDouble(); trunc(val) != val)
+					doThrow = true;
+			} else if (!value.isInteger())
+				doThrow = true;
+			break;
+		case QMetaType::QChar:
+		case QMetaType::QString:
+		case QMetaType::Char:
+		case QMetaType::QColor:
+		case QMetaType::QUrl:
+		case QMetaType::QFont:
+		case QMetaType::QUuid:
+			if (!value.isString())
+				doThrow = true;
+			break;
+		case QMetaType::Nullptr:
+			if (!value.isNull())
+				doThrow = true;
+			break;
+		case QMetaType::Float:
+		case QMetaType::Double:
+			if (!value.isDouble())
+				doThrow = true;
+			break;
+		default:
+			break;
+		}
+
+		if (doThrow) {
+			throw DeserializationException(QByteArray("Failed to deserialze JSON-value to type ") +
+										   QMetaTypeName(propertyType) +
+										   QByteArray("because the given JSON-value failed strict validation"));
+		}
+	}
+
+	// special case: QRegularExpression, is missing a converter (and cannot be registered)
+	if (propertyType == QMetaType::QRegularExpression &&
+		value.isString())
+		return QRegularExpression{value.toString()};
+	else
+		return value.toVariant();
+}

+ 788 - 0
jsonserializer/serializerbase.cpp~RFc998f2.TMP

@@ -0,0 +1,788 @@
+#include "serializerbase.h"
+#include "serializerbase_p.h"
+#include "exceptioncontext_p.h"
+
+#include <optional>
+#include <variant>
+#include <cmath>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QScopeGuard>
+
+#include "typeconverters/bitarrayconverter_p.h"
+#include "typeconverters/bytearrayconverter_p.h"
+#include "typeconverters/cborconverter_p.h"
+#include "typeconverters/datetimeconverter_p.h"
+#include "typeconverters/enumconverter_p.h"
+#include "typeconverters/gadgetconverter_p.h"
+#include "typeconverters/geomconverter_p.h"
+#include "typeconverters/legacygeomconverter_p.h"
+#include "typeconverters/listconverter_p.h"
+#include "typeconverters/localeconverter_p.h"
+#include "typeconverters/mapconverter_p.h"
+#include "typeconverters/multimapconverter_p.h"
+#include "typeconverters/objectconverter_p.h"
+#include "typeconverters/pairconverter_p.h"
+#include "typeconverters/smartpointerconverter_p.h"
+#include "typeconverters/stdchronodurationconverter_p.h"
+#include "typeconverters/stdoptionalconverter_p.h"
+#include "typeconverters/stdtupleconverter_p.h"
+#include "typeconverters/stdvariantconverter_p.h"
+#include "typeconverters/versionnumberconverter_p.h"
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+#ifndef NO_REGISTER_JSON_CONVERTERS
+namespace {
+void qtJsonSerializerRegisterTypes() {
+	QtJsonSerializer::registerTypes();
+}
+}
+Q_COREAPP_STARTUP_FUNCTION(qtJsonSerializerRegisterTypes);
+#endif
+
+Q_LOGGING_CATEGORY(QtJsonSerializer::logSerializer, "qt.jsonserializer.serializer")
+Q_LOGGING_CATEGORY(QtJsonSerializer::logSerializerExtractor, "qt.jsonserializer.serializer.extractor")
+
+namespace {
+
+class LogTag {
+public:
+	inline LogTag(QCborTag tag) : tag{tag} {}
+	inline operator QCborTag() const { return tag; }
+private:
+	QCborTag tag;
+};
+
+QDebug operator<<(QDebug debug, LogTag tag) {
+	if (tag != TypeConverter::NoTag)
+		debug << ", CBOR-Tag" << static_cast<QCborTag>(tag);
+	return debug;
+}
+
+}
+
+SerializerBase::SerializerBase(QObject *parent) :
+	SerializerBase{*new SerializerBasePrivate{}, parent}
+{}
+
+SerializerBase::SerializerBase(SerializerBasePrivate &dd, QObject *parent) :
+	QObject{dd, parent}
+{}
+
+void SerializerBase::registerExtractor(int metaTypeId, const QSharedPointer<TypeExtractor> &extractor)
+{
+	SerializerBasePrivate::extractors.add(metaTypeId, extractor);
+	qCDebug(logSerializerExtractor) << "Added extractor for type:" << QMetaTypeName(metaTypeId);
+}
+
+bool SerializerBase::allowDefaultNull() const
+{
+	Q_D(const SerializerBase);
+	return d->allowNull;
+}
+
+bool SerializerBase::keepObjectName() const
+{
+	Q_D(const SerializerBase);
+	return d->keepObjectName;
+}
+
+bool SerializerBase::enumAsString() const
+{
+	Q_D(const SerializerBase);
+	return d->enumAsString;
+}
+
+bool SerializerBase::versionAsString() const
+{
+	Q_D(const SerializerBase);
+	return d->versionAsString;
+}
+
+bool SerializerBase::dateAsTimeStamp() const
+{
+	Q_D(const SerializerBase);
+	return d->dateAsTimeStamp;
+}
+
+bool SerializerBase::useBcp47Locale() const
+{
+	Q_D(const SerializerBase);
+	return d->useBcp47Locale;
+}
+
+SerializerBase::ValidationFlags SerializerBase::validationFlags() const
+{
+	Q_D(const SerializerBase);
+	return d->validationFlags;
+}
+
+SerializerBase::Polymorphing SerializerBase::polymorphing() const
+{
+	Q_D(const SerializerBase);
+	return d->polymorphing;
+}
+
+SerializerBase::MultiMapMode SerializerBase::multiMapMode() const
+{
+	Q_D(const SerializerBase);
+	return d->multiMapMode;
+}
+
+bool SerializerBase::ignoresStoredAttribute() const
+{
+	Q_D(const SerializerBase);
+	return d->ignoreStoredAttribute;
+}
+
+void SerializerBase::addJsonTypeConverterFactory(TypeConverterFactory *factory)
+{
+	QWriteLocker _{&SerializerBasePrivate::typeConverterFactoryLock};
+	SerializerBasePrivate::typeConverterFactories.append(factory);
+	qCDebug(logSerializer) << "Added new global converter factory:" << factory;
+}
+
+void SerializerBase::addJsonTypeConverter(const QSharedPointer<TypeConverter> &converter)
+{
+	Q_D(SerializerBase);
+	Q_ASSERT_X(converter, Q_FUNC_INFO, "converter must not be null!");
+	converter->setHelper(this);
+	d->typeConverters.insertSorted(converter);
+	d->serCache.clear();
+	d->deserCache.clear();
+	qCDebug(logSerializer) << "Added new local converter:" << converter->name();
+}
+
+void SerializerBase::setAllowDefaultNull(bool allowDefaultNull)
+{
+	Q_D(SerializerBase);
+	if(d->allowNull == allowDefaultNull)
+		return;
+
+	d->allowNull = allowDefaultNull;
+	emit allowDefaultNullChanged(d->allowNull, {});
+}
+
+void SerializerBase::setKeepObjectName(bool keepObjectName)
+{
+	Q_D(SerializerBase);
+	if(d->keepObjectName == keepObjectName)
+		return;
+
+	d->keepObjectName = keepObjectName;
+	emit keepObjectNameChanged(d->keepObjectName, {});
+}
+
+void SerializerBase::setEnumAsString(bool enumAsString)
+{
+	Q_D(SerializerBase);
+	if(d->enumAsString == enumAsString)
+		return;
+
+	d->enumAsString = enumAsString;
+	emit enumAsStringChanged(d->enumAsString, {});
+}
+
+void SerializerBase::setVersionAsString(bool versionAsString)
+{
+	Q_D(SerializerBase);
+	if(d->versionAsString == versionAsString)
+		return;
+
+	d->versionAsString = versionAsString;
+	emit versionAsStringChanged(d->versionAsString, {});
+}
+
+void SerializerBase::setDateAsTimeStamp(bool dateAsTimeStamp)
+{
+	Q_D(SerializerBase);
+	if(d->dateAsTimeStamp == dateAsTimeStamp)
+		return;
+
+	d->dateAsTimeStamp = dateAsTimeStamp;
+	emit dateAsTimeStampChanged(d->dateAsTimeStamp, {});
+}
+
+void SerializerBase::setUseBcp47Locale(bool useBcp47Locale)
+{
+	Q_D(SerializerBase);
+	if(d->useBcp47Locale == useBcp47Locale)
+		return;
+
+	d->useBcp47Locale = useBcp47Locale;
+	emit useBcp47LocaleChanged(d->useBcp47Locale, {});
+}
+
+void SerializerBase::setValidationFlags(ValidationFlags validationFlags)
+{
+	Q_D(SerializerBase);
+	if(d->validationFlags == validationFlags)
+		return;
+
+	d->validationFlags = validationFlags;
+	emit validationFlagsChanged(d->validationFlags, {});
+}
+
+void SerializerBase::setPolymorphing(SerializerBase::Polymorphing polymorphing)
+{
+	Q_D(SerializerBase);
+	if(d->polymorphing == polymorphing)
+		return;
+
+	d->polymorphing = polymorphing;
+	emit polymorphingChanged(d->polymorphing, {});
+}
+
+void SerializerBase::setMultiMapMode(SerializerBase::MultiMapMode multiMapMode)
+{
+	Q_D(SerializerBase);
+	if(d->multiMapMode == multiMapMode)
+		return;
+
+	d->multiMapMode = multiMapMode;
+	emit multiMapModeChanged(d->multiMapMode, {});
+}
+
+void SerializerBase::setIgnoreStoredAttribute(bool ignoreStoredAttribute)
+{
+	Q_D(SerializerBase);
+	if(d->ignoreStoredAttribute == ignoreStoredAttribute)
+		return;
+
+	d->ignoreStoredAttribute = ignoreStoredAttribute;
+	emit ignoreStoredAttributeChanged(d->ignoreStoredAttribute, {});
+}
+
+QVariant SerializerBase::getProperty(const char *name) const
+{
+	return property(name);
+}
+
+QSharedPointer<const TypeExtractor> SerializerBase::extractor(int metaTypeId) const
+{
+	const auto extractor = SerializerBasePrivate::extractors.get(metaTypeId);
+	if (extractor)
+		qCDebug(logSerializerExtractor) << "Found extractor for type:" << QMetaTypeName(metaTypeId);
+	else
+		qCDebug(logSerializerExtractor) << "Unable to find extractor for type:" << QMetaTypeName(metaTypeId);
+	return extractor;
+}
+
+QCborValue SerializerBase::serializeSubtype(const QMetaProperty &property, const QVariant &value) const
+{
+	Q_D(const SerializerBase);
+	ExceptionContext ctx(property);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	if (property.isEnumType()) {
+		const auto enumId = d->getEnumId(property.enumerator(), true);
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Serializing subtype property" << property.name()
+							   << "of enum type" << QMetaTypeName(enumId);
+		return serializeVariant(enumId, value);
+	} else {
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Serializing subtype property" << property.name()
+							   << "of type" << QMetaTypeName(property.userType());
+		return serializeVariant(property.userType(), value);
+	}
+}
+
+QCborValue SerializerBase::serializeSubtype(int propertyType, const QVariant &value, const QByteArray &traceHint) const
+{
+	ExceptionContext ctx(propertyType, traceHint);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+						   << "Serializing subtype property" << traceHint
+						   << "of type" << QMetaTypeName(propertyType);
+	return serializeVariant(propertyType, value);
+}
+
+QVariant SerializerBase::deserializeSubtype(const QMetaProperty &property, const QCborValue &value, QObject *parent) const
+{
+	Q_D(const SerializerBase);
+	ExceptionContext ctx(property);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	if (property.isEnumType()) {
+		const auto enumId = d->getEnumId(property.enumerator(), false);
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Deserializing subtype property" << property.name()
+							   << "of enum type" << QMetaTypeName(enumId);
+		return deserializeVariant(enumId, value, parent, true);
+	} else {
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "Deserializing subtype property" << property.name()
+							   << "of type" << QMetaTypeName(property.userType());
+		return deserializeVariant(property.userType(), value, parent);
+	}
+}
+
+QVariant SerializerBase::deserializeSubtype(int propertyType, const QCborValue &value, QObject *parent, const QByteArray &traceHint) const
+{
+	ExceptionContext ctx(propertyType, traceHint);
+	auto logGuard = qScopeGuard([](){
+		qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+							   << "done";
+	});
+	qCDebug(logSerializer) << QByteArray{">"}.repeated(ExceptionContext::currentDepth()).constData()
+						   << "Deserializing subtype property" << traceHint
+						   << "of type" << QMetaTypeName(propertyType);
+	return deserializeVariant(propertyType, value, parent);
+}
+
+QCborValue SerializerBase::serializeVariant(int propertyType, const QVariant &value) const
+{
+	Q_D(const SerializerBase);
+	// first: find a converter and convert to cbor
+	auto converter = d->findSerConverter(propertyType);
+	QCborValue res;
+	if (converter)
+		res = converter->serialize(propertyType, value);
+	else
+		res = d->serializeValue(propertyType, value);
+
+	// second: check if an override tag is given, and if yes, override the normal tag
+	if (const auto mTag = typeTag(propertyType); mTag != TypeConverter::NoTag)
+		return {mTag, res.isTag() ? res.taggedValue() : res};
+	else
+		return res;
+}
+
+QVariant SerializerBase::deserializeVariant(int propertyType, const QCborValue &value, QObject *parent, bool skipConversion) const
+{
+	Q_D(const SerializerBase);
+	// first: find a converter and convert the data to QVariant
+	auto converter = d->findDeserConverter(propertyType,
+										   value.isTag() ? value.tag() : TypeConverter::NoTag,
+										   value.isTag() ? value.taggedValue().type() : value.type());
+
+	QVariant variant;
+	if (converter) {
+		if (jsonMode())
+			variant = converter->deserializeJson(propertyType, value, parent);
+		else
+			variant = converter->deserializeCbor(propertyType, value, parent);
+	} else {
+		if (jsonMode())
+			variant = d->deserializeJsonValue(propertyType, value);
+		else
+			variant = d->deserializeCborValue(propertyType, value);
+	}
+
+	// second: if the type was given, enforce a conversion to that type (expect if skipped)
+	if(!skipConversion && propertyType != QMetaType::UnknownType) {
+		auto vType = variant.typeName();
+
+		// exclude special values that can convert from null, but should not do so
+		auto allowConvert = true;
+		switch (propertyType) {
+		case QMetaType::QString:
+		case QMetaType::QByteArray:
+			if (value.isNull())
+				allowConvert = false;
+			break;
+		default:
+			break;
+		}
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+		if(allowConvert && variant.canConvert(propertyType) && variant.convert(propertyType))
+#else
+		if(allowConvert && variant.canConvert(QMetaType(propertyType)) && variant.convert(QMetaType(propertyType)))
+#endif
+			return variant;
+		else if(d->allowNull && value.isNull())
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+			return QVariant{propertyType, nullptr};
+#else
+			return QVariant{QMetaType(propertyType), nullptr};
+#endif
+		else {
+			throw DeserializationException(QByteArray("Failed to convert deserialized variant of type ") +
+										   (vType ? vType : "<unknown>") +
+										   QByteArray(" to property type ") +
+										   QMetaTypeName(propertyType) +
+										   QByteArray(". Make shure to register converters with the QJsonSerializer::register* methods"));
+		}
+	} else
+		return variant;
+}
+
+// ------------- private implementation -------------
+
+SerializerBasePrivate::ThreadSafeStore<TypeExtractor> SerializerBasePrivate::extractors;
+QReadWriteLock SerializerBasePrivate::typeConverterFactoryLock;
+QList<TypeConverterFactory*> SerializerBasePrivate::typeConverterFactories {
+	new TypeConverterStandardFactory<BitArrayConverter>{},
+	new TypeConverterStandardFactory<BytearrayConverter>{},
+	new TypeConverterStandardFactory<CborConverter>{},
+	new TypeConverterStandardFactory<DateTimeConverter>{},
+	new TypeConverterStandardFactory<EnumConverter>{},
+	new TypeConverterStandardFactory<GadgetConverter>{},
+	new TypeConverterStandardFactory<GeomConverter>{},
+	new TypeConverterStandardFactory<ListConverter>{},
+	new TypeConverterStandardFactory<LocaleConverter>{},
+	new TypeConverterStandardFactory<MapConverter>{},
+	new TypeConverterStandardFactory<MultiMapConverter>{},
+	new TypeConverterStandardFactory<ObjectConverter>{},
+	new TypeConverterStandardFactory<PairConverter>{},
+	new TypeConverterStandardFactory<SmartPointerConverter>{},
+	new TypeConverterStandardFactory<StdChronoDurationConverter>{},
+	new TypeConverterStandardFactory<StdOptionalConverter>{},
+	new TypeConverterStandardFactory<StdTupleConverter>{},
+	new TypeConverterStandardFactory<StdVariantConverter>{},
+	new TypeConverterStandardFactory<VersionNumberConverter>{},
+
+	new TypeConverterStandardFactory<LegacyGeomConverter>{}
+};
+
+QSharedPointer<TypeConverter> SerializerBasePrivate::findSerConverter(int propertyType) const
+{
+	// first: update converters from factories
+	updateConverterStore();
+
+	// second: check if already cached
+	if (auto converter = serCache.get(propertyType); converter) {
+		qCDebug(logSerializer) << "Found cached serialization converter" << converter->name()
+							   << "for type:" <<  QMetaTypeName(propertyType);
+		return converter;
+	}
+
+	// third: check if the list of explicit converters has a matching one
+	QReadLocker cLocker{&typeConverters.lock};
+	for (const auto &converter : qAsConst(typeConverters.store)) {
+		if (converter && converter->canConvert(propertyType)) {
+			qCDebug(logSerializer) << "Found and cached serialization converter" << converter->name()
+								   << "for type:" <<  QMetaTypeName(propertyType);
+			// add converter to cache and return it
+			serCache.add(propertyType, converter);
+			return converter;
+		}
+	}
+
+	// fourth: no converter found: return default converter
+	qCDebug(logSerializer) << "Unable to find serialization converte for type:" <<  QMetaTypeName(propertyType)
+						   << "- falling back to default QVariant to CBOR conversion";
+	return nullptr;
+}
+
+QSharedPointer<TypeConverter> SerializerBasePrivate::findDeserConverter(int &propertyType, QCborTag tag, QCborValue::Type type) const
+{
+	Q_Q(const SerializerBase);
+	// first: update converters from factories
+	updateConverterStore();
+
+	// second: if no property type is given, try out any types associated with the tag
+	if (propertyType == QMetaType::UnknownType && tag != TypeConverter::NoTag) {
+		const auto tList = q->typesForTag(tag);
+		for (auto typeId : tList) {
+			// if any of those types has a working converter, just use that one
+			auto res = findDeserConverter(typeId, tag, type);
+			if (res) {
+				propertyType = typeId;
+				return res;
+			}
+		}
+	}
+
+	// third: check if already cached
+	if (auto converter = deserCache.get(propertyType);
+		converter && converter->canDeserialize(propertyType, tag, type) > 0) {
+		qCDebug(logSerializer) << "Found cached deserialization converter" << converter->name()
+							   << "for type" <<  QMetaTypeName(propertyType)
+							   << LogTag{tag}
+							   << "and CBOR-type" << type;
+		return converter;
+	}
+
+	// fourth: check if the list of explicit converters has a matching one
+	QReadLocker cLocker{&typeConverters.lock};
+	auto throwWrongTag = false;
+	std::optional<std::pair<QSharedPointer<TypeConverter>, int>> guessConverter;
+	for (const auto &converter : qAsConst(typeConverters.store)) {
+		if (converter) {
+			auto testType = propertyType;
+			switch (converter->canDeserialize(testType, tag, type)) {
+			case TypeConverter::Negative:
+				continue;
+			case TypeConverter::WrongTag:
+				throwWrongTag = true;
+				continue;
+			case TypeConverter::Guessed:
+				if (!guessConverter)
+					guessConverter = std::make_pair(converter, testType);
+				continue;
+			case TypeConverter::Positive:
+				break;
+			}
+
+			// add converter to cache (only happens for positive cases)
+			deserCache.add(propertyType, converter);
+			qCDebug(logSerializer) << "Found and cached deserialization converter" << converter->name()
+								   << "for type" <<  QMetaTypeName(propertyType)
+								   << LogTag{tag}
+								   << "and CBOR-type" << type;
+			return converter;
+		}
+	}
+	cLocker.unlock();
+
+	// fifth: if a guessed converter is available, use that one
+	if (guessConverter) {
+		// extract converter from info;
+		auto &[converter, newType] = *guessConverter;
+		// if valid, add to cache, set the type and return
+		if (converter) {
+			// add converter to list and cache
+			propertyType = newType;
+			deserCache.add(propertyType, converter);
+			qCDebug(logSerializer) << "Found and cached deserialization converter" << converter->name()
+								   << "by guessing the data with CBOR-tag" << tag
+								   << "and CBOR-type" << type
+								   << "is of type" << QMetaTypeName(propertyType);
+			return converter;
+		}
+	}
+
+	// sixth: if a wrong tag mark was set, throw an expection
+	if (throwWrongTag) {
+		throw DeserializationException{QByteArray{"Found converter able to handle data of type "} +
+									   QMetaTypeName(propertyType) +
+									   ", but the given CBOR tag " +
+									   QByteArray::number(static_cast<quint64>(tag)) +
+									   " is not convertible to that type."};
+	}
+
+	// seventh: no converter found: return default converter
+	qCDebug(logSerializer) << "Unable to find deserialization converte for type" <<  QMetaTypeName(propertyType)
+						   << LogTag{tag}
+						   << "and CBOR-type" << type
+						   << "- falling back to default CBOR to QVariant conversion";
+	return nullptr;
+}
+
+void SerializerBasePrivate::updateConverterStore() const
+{
+	Q_Q(const SerializerBase);
+	QReadLocker fLocker{&typeConverterFactoryLock};
+	if (typeConverterFactories.size() > typeConverters.factoryOffset.loadAcquire()) {
+		QWriteLocker cLocker{&typeConverters.lock};
+		auto max = typeConverterFactories.size();
+		for (auto i = typeConverters.factoryOffset.loadAcquire(); i < max; ++i) {
+			auto converter = typeConverterFactories[i]->createConverter();
+			if (converter) {
+				converter->setHelper(q);
+				typeConverters.insertSorted(converter, cLocker);
+				serCache.clear();
+				deserCache.clear();
+				qCDebug(logSerializer) << "Found and added new global converter:" << converter->name();
+			}
+		}
+		typeConverters.factoryOffset.storeRelease(typeConverterFactories.size());
+	}
+}
+
+int SerializerBasePrivate::getEnumId(QMetaEnum metaEnum, bool ser) const
+{
+	QByteArray eName = metaEnum.name();
+	if (const QByteArray scope = metaEnum.scope(); !scope.isEmpty())
+		eName = scope + "::" + eName;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	const auto eTypeId = QMetaType::type(eName);
+#else
+	const auto eTypeId = QMetaType::fromName(eName).id();
+#endif
+	if (eTypeId == QMetaType::UnknownType) {
+		if (ser)
+			throw SerializationException{"Unable to determine typeid of meta enum " + eName};
+		else
+			throw DeserializationException{"Unable to determine typeid of meta enum " + eName};
+	} else
+		return eTypeId;
+}
+
+QCborValue SerializerBasePrivate::serializeValue(int propertyType, const QVariant &value) const
+{
+	Q_UNUSED(propertyType)
+	return QCborValue::fromVariant(value);
+}
+
+QVariant SerializerBasePrivate::deserializeCborValue(int propertyType, const QCborValue &value) const
+{
+	Q_Q(const SerializerBase);
+	// perform strict validations
+	if (validationFlags.testFlag(ValidationFlag::StrictBasicTypes)) {
+		auto doThrow = false;
+
+		QList<QCborTag> expectedTags;
+		const auto testValue = value.isTag() ? value.taggedValue() : value;
+		switch (propertyType) {
+		case QMetaType::Bool:
+			if (!testValue.isBool())
+				doThrow = true;
+			break;
+		case QMetaType::Int:
+		case QMetaType::UInt:
+		case QMetaType::Long:
+		case QMetaType::LongLong:
+		case QMetaType::Short:
+		case QMetaType::ULong:
+		case QMetaType::ULongLong:
+		case QMetaType::UShort:
+		case QMetaType::SChar:
+		case QMetaType::UChar:
+			if (!testValue.isInteger())
+				doThrow = true;
+			break;
+		case QMetaType::Float:
+		case QMetaType::Double:
+			if (!testValue.isDouble())
+				doThrow = true;
+			break;
+		case QMetaType::Char:
+		case QMetaType::QChar:
+		case QMetaType::QString:
+			expectedTags = {
+				TypeConverter::NoTag,
+				static_cast<QCborTag>(QCborKnownTags::Base64),
+				static_cast<QCborTag>(QCborKnownTags::Base64url)
+			};
+			Q_FALLTHROUGH();
+		case QMetaType::QColor:
+		case QMetaType::QFont:
+			if (!testValue.isString())
+				doThrow = true;
+			break;
+		case QMetaType::QByteArray:
+			expectedTags = {
+				TypeConverter::NoTag,
+				static_cast<QCborTag>(QCborKnownTags::ExpectedBase64),
+				static_cast<QCborTag>(QCborKnownTags::ExpectedBase64url),
+				static_cast<QCborTag>(QCborKnownTags::ExpectedBase16)
+			};
+			if (!testValue.isByteArray())
+				doThrow = true;
+			break;
+		case QMetaType::Nullptr:
+			if (!testValue.isNull())
+				doThrow = true;
+			break;
+		case QMetaType::QUrl:
+			if (!value.isUrl()) {
+				if (testValue.isString())
+					expectedTags = {static_cast<QCborTag>(QCborKnownTags::Url)};
+				else
+					doThrow = true;
+			}
+			break;
+		case QMetaType::QUuid:
+			if (!value.isUuid()) {
+				if (testValue.isByteArray())
+					expectedTags = {static_cast<QCborTag>(QCborKnownTags::Uuid)};
+				else
+					doThrow = true;
+			}
+			break;
+		case QMetaType::QRegularExpression:
+			if (!value.isRegularExpression()) {
+				if (testValue.isString())
+					expectedTags = {static_cast<QCborTag>(QCborKnownTags::RegularExpression)};
+				else
+					doThrow = true;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (const auto mTag = q->typeTag(propertyType);
+			mTag != TypeConverter::NoTag &&
+			mTag != value.tag())
+			doThrow = true;
+		else if (!expectedTags.isEmpty() &&
+				 !expectedTags.contains(value.tag()))
+			doThrow = true;
+
+		if (doThrow) {
+			throw DeserializationException(QByteArray("Failed to deserialze CBOR-value to type ") +
+										   QMetaTypeName(propertyType) +
+										   QByteArray(" because the given CBOR-value failed strict validation"));
+		}
+	}
+
+	return value.toVariant();
+}
+
+QVariant SerializerBasePrivate::deserializeJsonValue(int propertyType, const QCborValue &value) const
+{
+	// perform strict validations
+	if (validationFlags.testFlag(ValidationFlag::StrictBasicTypes)) {
+		auto doThrow = false;
+		switch (propertyType) {
+		case QMetaType::Bool:
+			if (!value.isBool())
+				doThrow = true;
+			break;
+		case QMetaType::Int:
+		case QMetaType::UInt:
+		case QMetaType::Long:
+		case QMetaType::LongLong:
+		case QMetaType::Short:
+		case QMetaType::ULong:
+		case QMetaType::ULongLong:
+		case QMetaType::UShort:
+		case QMetaType::SChar:
+		case QMetaType::UChar:
+			if (value.isDouble()) {
+				if (auto val = value.toDouble(); trunc(val) != val)
+					doThrow = true;
+			} else if (!value.isInteger())
+				doThrow = true;
+			break;
+		case QMetaType::QChar:
+		case QMetaType::QString:
+		case QMetaType::Char:
+		case QMetaType::QColor:
+		case QMetaType::QUrl:
+		case QMetaType::QFont:
+		case QMetaType::QUuid:
+			if (!value.isString())
+				doThrow = true;
+			break;
+		case QMetaType::Nullptr:
+			if (!value.isNull())
+				doThrow = true;
+			break;
+		case QMetaType::Float:
+		case QMetaType::Double:
+			if (!value.isDouble())
+				doThrow = true;
+			break;
+		default:
+			break;
+		}
+
+		if (doThrow) {
+			throw DeserializationException(QByteArray("Failed to deserialze JSON-value to type ") +
+										   QMetaTypeName(propertyType) +
+										   QByteArray("because the given JSON-value failed strict validation"));
+		}
+	}
+
+	// special case: QRegularExpression, is missing a converter (and cannot be registered)
+	if (propertyType == QMetaType::QRegularExpression &&
+		value.isString())
+		return QRegularExpression{value.toString()};
+	else
+		return value.toVariant();
+}

+ 351 - 0
jsonserializer/serializerbase.h

@@ -0,0 +1,351 @@
+#ifndef QTJSONSERIALIZER_SERIALIZERBASE_H
+#define QTJSONSERIALIZER_SERIALIZERBASE_H
+
+#include "qtjsonserializer_global.h"
+
+#include "exception.h"
+#include "metawriters.h"
+#include "qtjsonserializer_helpertypes.h"
+#include "typeconverter.h"
+#include "typeextractors.h"
+
+#include <tuple>
+#include <optional>
+#include <variant>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qcborvalue.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qlist.h>
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore/qlinkedlist.h>
+#endif
+
+#include <QtCore/qvector.h>
+#include <QtCore/qset.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qmap.h>
+
+namespace QtJsonSerializer {
+
+class SerializerBasePrivate;
+//! A base class for the CBOR/JSON serializers
+class Q_JSONSERIALIZER_EXPORT SerializerBase : public QObject, protected TypeConverter::SerializationHelper
+{
+	Q_OBJECT
+
+	//! Specifies whether null for value types is allowed or not
+	Q_PROPERTY(bool allowDefaultNull READ allowDefaultNull WRITE setAllowDefaultNull NOTIFY allowDefaultNullChanged)
+	//! Specifies whether the `objectName` property of QObjects should be serialized
+	Q_PROPERTY(bool keepObjectName READ keepObjectName WRITE setKeepObjectName NOTIFY keepObjectNameChanged)
+	//! Specifies whether enums should be serialized as integer or as string
+	Q_PROPERTY(bool enumAsString READ enumAsString WRITE setEnumAsString NOTIFY enumAsStringChanged)
+	//! Specifies whether enums should be serialized as array of integers or as string
+	Q_PROPERTY(bool versionAsString READ versionAsString WRITE setVersionAsString NOTIFY versionAsStringChanged)
+	//! Specifies whether datetimes should be serialized as datetime string or as unix timestamp
+	Q_PROPERTY(bool dateAsTimeStamp READ dateAsTimeStamp WRITE setDateAsTimeStamp NOTIFY dateAsTimeStampChanged)
+	//! Specifies whether serializing a QLocale should use the bcp47 format
+	Q_PROPERTY(bool useBcp47Locale READ useBcp47Locale WRITE setUseBcp47Locale NOTIFY useBcp47LocaleChanged)
+	//! Specifies how strictly the serializer should verify data when deserializing
+	Q_PROPERTY(ValidationFlags validationFlags READ validationFlags WRITE setValidationFlags NOTIFY validationFlagsChanged)
+	//! Specifies how the serializer should treat polymorphism for QObject classes
+	Q_PROPERTY(Polymorphing polymorphing READ polymorphing WRITE setPolymorphing NOTIFY polymorphingChanged)
+	//! Specifies how multi maps and sets should be serialized
+	Q_PROPERTY(MultiMapMode multiMapMode READ multiMapMode WRITE setMultiMapMode NOTIFY multiMapModeChanged)
+	//! Specifies whether the STORED attribute on properties has any effect
+	Q_PROPERTY(bool ignoreStoredAttribute READ ignoresStoredAttribute WRITE setIgnoreStoredAttribute NOTIFY ignoreStoredAttributeChanged)
+
+public:
+	//! Flags to specify how strict the serializer should validate when deserializing
+	enum class ValidationFlag {
+		StandardValidation = 0x00, //!< Do not perform extra validation, only make sure types are valid and compatible
+		NoExtraProperties = 0x01, //!< Make sure the json does not contain any properties that are not in the type to deserialize it to
+		AllProperties = 0x02, //!< Make sure all properties of the type have a value in the deserialized json data
+		StrictBasicTypes = 0x04, //!< Make shure basic types (string, int, ...) are actually of those types, instead of accepting all that are convertible
+
+		FullPropertyValidation = (NoExtraProperties | AllProperties), //!< Validate properties are exactly the same as declared
+		FullValidation = (FullPropertyValidation | StrictBasicTypes), //!< Validate everything
+	};
+	Q_DECLARE_FLAGS(ValidationFlags, ValidationFlag)
+	Q_FLAG(ValidationFlags)
+
+	//! Enum to specify the modes of polymorphism
+	enum class Polymorphing {
+		Disabled, //!< Do not serialize polymorphic and ignore information about classes in json
+		Enabled, //!< Use polymorphism where declared by the classes/json
+		Forced //!< Treat every object polymorphic, and required the class information to be present in json
+	};
+	Q_ENUM(Polymorphing)
+
+	//! Enum to specify how multi maps and sets should be serialized
+	enum class MultiMapMode {
+		Map, //!< Store them as json object, with each element beeing a json array containing the actual values
+		List, //!< Store a list of pairs, where for each pair the first element is the key and the second the value
+		DenseMap //!< Just like Map, but entries with just one value are stored as that value, instead of an array with one element
+	};
+	Q_ENUM(MultiMapMode)
+
+	//! Registers a custom extractor for the given type
+	template<typename TType, typename TExtractor>
+	static void registerExtractor();
+	//! @copybrief SerializerBase::registerExtractor
+	static void registerExtractor(int metaTypeId, const QSharedPointer<TypeExtractor> &extractor);
+
+	//! Registers a custom type for list converisons
+	template<typename T>
+	static inline void registerListConverters();
+	//! Registers a custom type for set converisons
+	template<typename T>
+	static inline void registerSetConverters();
+	//! Registers a custom type for map converisons
+	template<typename TKey, typename TValue, bool mapTypes = true, bool hashTypes = true>
+	static inline void registerMapConverters();
+	//! Registers a custom type for QSharedPointer and QPointer converisons
+	template<typename T>
+	static inline void registerPointerConverters();
+	//! Registers a custom type for list, set map and optional converisons. Also include pointer converters, if applicable
+	template<typename T>
+	static inline void registerBasicConverters();
+	//! Registers two types for pair conversion
+	template<typename T, typename U>
+	static inline void registerPairConverters();
+	//! Registers a number of types for std::tuple conversion
+	template<typename... TArgs>
+	static inline void registerTupleConverters();
+	//! Registers a custom type for std::optional converisons
+	template<typename T>
+	static inline void registerOptionalConverters();
+	//! Registers a custom type for std::variant converisons
+	template<typename... TArgs>
+	static inline void registerVariantConverters();
+
+	//! Serializes a given variant to either CBOR or JSON, depending on the actual instance
+	virtual std::variant<QCborValue, QJsonValue> serializeGeneric(const QVariant &value) const = 0;
+	//! Deserializes CBOR or JSON, depending on the actual instance, to variant
+	virtual QVariant deserializeGeneric(const std::variant<QCborValue, QJsonValue> &value, int metaTypeId, QObject *parent = nullptr) const = 0;
+
+	//! @readAcFn{QJsonSerializer::allowDefaultNull}
+	bool allowDefaultNull() const;
+	//! @readAcFn{QJsonSerializer::keepObjectName}
+	bool keepObjectName() const;
+	//! @readAcFn{QJsonSerializer::enumAsString}
+	bool enumAsString() const;
+	//! @readAcFn{QJsonSerializer::versionAsString}
+	bool versionAsString() const;
+	//! @readAcFn{QJsonSerializer::dateAsTimeStamp}
+	bool dateAsTimeStamp() const;
+	//! @readAcFn{QJsonSerializer::useBcp47Locale}
+	bool useBcp47Locale() const;
+	//! @readAcFn{QJsonSerializer::validationFlags}
+	ValidationFlags validationFlags() const;
+	//! @readAcFn{QJsonSerializer::polymorphing}
+	Polymorphing polymorphing() const;
+	//! @readAcFn{QJsonSerializer::multiMapMode}
+	MultiMapMode multiMapMode() const;
+	//! @readAcFn{QJsonSerializer::ignoreStoredAttribute}
+	bool ignoresStoredAttribute() const;
+
+	//! Globally registers a converter factory to provide converters for all QJsonSerializer instances
+	template <typename TConverter, int Priority = TypeConverter::Priority::Standard>
+	static void addJsonTypeConverterFactory();
+	//! @copybrief SerializerBase::addJsonTypeConverterFactory()
+	static void addJsonTypeConverterFactory(TypeConverterFactory *factory);
+
+	//! Adds a custom type converter to this serializer
+	template <typename TConverter>
+	void addJsonTypeConverter();
+	//! @copybrief SerializerBase::addJsonTypeConverter()
+	void addJsonTypeConverter(const QSharedPointer<TypeConverter> &converter);
+
+public Q_SLOTS:
+	//! @writeAcFn{QJsonSerializer::allowDefaultNull}
+	void setAllowDefaultNull(bool allowDefaultNull);
+	//! @writeAcFn{QJsonSerializer::keepObjectName}
+	void setKeepObjectName(bool keepObjectName);
+	//! @writeAcFn{QJsonSerializer::enumAsString}
+	void setEnumAsString(bool enumAsString);
+	//! @writeAcFn{QJsonSerializer::versionAsString}
+	void setVersionAsString(bool versionAsString);
+	//! @writeAcFn{QJsonSerializer::dateAsTimeStamp}
+	void setDateAsTimeStamp(bool dateAsTimeStamp);
+	//! @writeAcFn{QJsonSerializer::useBcp47Locale}
+	void setUseBcp47Locale(bool useBcp47Locale);
+	//! @writeAcFn{QJsonSerializer::validationFlags}
+	void setValidationFlags(ValidationFlags validationFlags);
+	//! @writeAcFn{QJsonSerializer::polymorphing}
+	void setPolymorphing(Polymorphing polymorphing);
+	//! @writeAcFn{QJsonSerializer::multiMapMode}
+	void setMultiMapMode(MultiMapMode multiMapMode);
+	//! @writeAcFn{QJsonSerializer::ignoreStoredAttribute}
+	void setIgnoreStoredAttribute(bool ignoreStoredAttribute);
+
+Q_SIGNALS:
+	//! @notifyAcFn{QJsonSerializer::allowDefaultNull}
+	void allowDefaultNullChanged(bool allowDefaultNull, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::keepObjectName}
+	void keepObjectNameChanged(bool keepObjectName, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::enumAsString}
+	void enumAsStringChanged(bool enumAsString, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::versionAsString}
+	void versionAsStringChanged(bool versionAsString, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::dateAsTimeStamp}
+	void dateAsTimeStampChanged(bool dateAsTimeStamp, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::useBcp47Locale}
+	void useBcp47LocaleChanged(bool useBcp47Locale, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::validationFlags}
+	void validationFlagsChanged(ValidationFlags validationFlags, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::polymorphing}
+	void polymorphingChanged(Polymorphing polymorphing, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::multiMapMode}
+	void multiMapModeChanged(MultiMapMode multiMapMode, QPrivateSignal);
+	//! @notifyAcFn{QJsonSerializer::ignoreStoredAttribute}
+	void ignoreStoredAttributeChanged(bool ignoreStoredAttribute, QPrivateSignal);
+
+protected:
+	//! Default constructor
+	explicit SerializerBase(QObject *parent = nullptr);
+	//! @private
+	explicit SerializerBase(SerializerBasePrivate &dd, QObject *parent);
+
+	virtual QList<int> typesForTag(QCborTag tag) const = 0;
+
+	// protected implementation -> internal use for the type converters
+	QVariant getProperty(const char *name) const override;
+	QSharedPointer<const TypeExtractor> extractor(int metaTypeId) const override;
+	QCborValue serializeSubtype(const QMetaProperty &property, const QVariant &value) const override;
+	QCborValue serializeSubtype(int propertyType, const QVariant &value, const QByteArray &traceHint) const override;
+	QVariant deserializeSubtype(const QMetaProperty &property, const QCborValue &value, QObject *parent) const override;
+	QVariant deserializeSubtype(int propertyType, const QCborValue &value, QObject *parent, const QByteArray &traceHint) const override;
+
+	//! @private
+	QCborValue serializeVariant(int propertyType, const QVariant &value) const;
+	//! @private
+	QVariant deserializeVariant(int propertyType, const QCborValue &value, QObject *parent, bool skipConversion = false) const;
+
+private:
+	Q_DECLARE_PRIVATE(SerializerBase)
+
+	static void registerInverseTypedefImpl(int typeId, const char *normalizedTypeName);
+};
+
+//! A macro the mark a class as polymorphic
+#define Q_JSON_POLYMORPHIC(x) \
+	static_assert(std::is_same<decltype(x), bool>::value, "x must be bool"); \
+	Q_CLASSINFO("polymorphic", #x)
+
+// ------------- Generic Implementation -------------
+
+template<typename TType, typename TExtractor>
+void SerializerBase::registerExtractor()
+{
+	registerExtractor(qMetaTypeId<TType>(), QSharedPointer<TExtractor>::create());
+}
+
+template<typename T>
+void SerializerBase::registerListConverters()
+{
+	MetaWriters::SequentialWriter::registerWriter<QList, T>();
+#if !defined(QT_NO_LINKED_LIST) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+	MetaWriters::SequentialWriter::registerWriter<QLinkedList, T>();
+#endif
+	MetaWriters::SequentialWriter::registerWriter<QVector, T>();
+	MetaWriters::SequentialWriter::registerWriter<QStack, T>();
+	MetaWriters::SequentialWriter::registerWriter<QQueue, T>();
+}
+
+template<typename T>
+void SerializerBase::registerSetConverters()
+{
+	MetaWriters::SequentialWriter::registerWriter<QSet, T>();
+}
+
+template<typename TKey, typename TValue, bool mapTypes, bool hashTypes>
+void SerializerBase::registerMapConverters()
+{
+	if constexpr (mapTypes) {
+		MetaWriters::AssociativeWriter::registerWriter<QMap, TKey, TValue>();
+		MetaWriters::AssociativeWriter::registerWriter<QMultiMap, TKey, TValue>();
+	}
+	if constexpr (hashTypes) {
+		MetaWriters::AssociativeWriter::registerWriter<QHash, TKey, TValue>();
+		MetaWriters::AssociativeWriter::registerWriter<QMultiHash, TKey, TValue>();
+	}
+}
+
+template<typename T>
+void SerializerBase::registerPointerConverters()
+{
+	registerExtractor<QSharedPointer<T>, TypeExtractors::SmartPointerExtractor<QSharedPointer, T>>();
+	if constexpr (std::is_base_of_v<QObject, T>)
+		registerExtractor<QPointer<T>, TypeExtractors::SmartPointerExtractor<QPointer, T>>();
+}
+
+template<typename T>
+void SerializerBase::registerBasicConverters()
+{
+	if constexpr (std::is_base_of_v<QObject, T>) {
+		registerBasicConverters<T*>();
+		registerPointerConverters<T>();
+		registerBasicConverters<QSharedPointer<T>>();
+		registerBasicConverters<QPointer<T>>();
+	} else {
+		registerListConverters<T>();
+		registerSetConverters<T>();
+		registerMapConverters<QString, T>();
+	}
+}
+
+template<typename T1, typename T2>
+void SerializerBase::registerPairConverters()
+{
+	registerExtractor<QPair<T1, T2>, TypeExtractors::PairExtractor<QPair, T1, T2>>();
+	registerExtractor<std::pair<T1, T2>, TypeExtractors::PairExtractor<std::pair, T1, T2>>();
+}
+
+template<typename... TArgs>
+void SerializerBase::registerTupleConverters()
+{
+	registerExtractor<std::tuple<TArgs...>, TypeExtractors::TupleExtractor<TArgs...>>();
+}
+
+template<typename T>
+void SerializerBase::registerOptionalConverters()
+{
+	registerExtractor<std::optional<T>, TypeExtractors::OptionalExtractor<T>>();
+}
+
+template<typename... TArgs>
+void SerializerBase::registerVariantConverters()
+{
+	registerExtractor<std::variant<TArgs...>, TypeExtractors::VariantExtractor<TArgs...>>();
+}
+
+template<typename TConverter, int Priority>
+void SerializerBase::addJsonTypeConverterFactory()
+{
+	static_assert(std::is_base_of<TypeConverter, TConverter>::value, "T must implement QJsonTypeConverter");
+	addJsonTypeConverterFactory(new TypeConverterStandardFactory<TConverter, Priority>{});
+}
+
+template<typename TConverter>
+void SerializerBase::addJsonTypeConverter()
+{
+	static_assert(std::is_base_of<TypeConverter, TConverter>::value, "T must implement QJsonTypeConverter");
+	addJsonTypeConverter(QSharedPointer<TConverter>::create());
+}
+
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QtJsonSerializer::SerializerBase::ValidationFlags)
+
+Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(QMultiMap)
+Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(QMultiHash)
+
+//! @file serializerbase.h The SerializerBase header file
+#endif // QTJSONSERIALIZER_SERIALIZERBASE_H

+ 149 - 0
jsonserializer/serializerbase_p.h

@@ -0,0 +1,149 @@
+#ifndef QTJSONSERIALIZER_SERIALIZERBASE_P_H
+#define QTJSONSERIALIZER_SERIALIZERBASE_P_H
+
+#include "qtjsonserializer_global.h"
+#include "serializerbase.h"
+
+#include <QtCore/QReadWriteLock>
+#include <QtCore/QHash>
+#include <QtCore/QLoggingCategory>
+
+#include <QtCore/private/qobject_p.h>
+
+namespace QtJsonSerializer {
+
+class Q_JSONSERIALIZER_EXPORT SerializerBasePrivate : public QObjectPrivate
+{
+	Q_DECLARE_PUBLIC(SerializerBase)
+
+public:
+	using ValidationFlag = SerializerBase::ValidationFlag;
+	using ValidationFlags = SerializerBase::ValidationFlags;
+	using Polymorphing = SerializerBase::Polymorphing;
+	using MultiMapMode = SerializerBase::MultiMapMode;
+
+	template <typename TConverter>
+	class ThreadSafeStore {
+	public:
+		ThreadSafeStore() = default;
+		ThreadSafeStore(std::initializer_list<std::pair<int, QSharedPointer<TConverter>>> initData);
+
+		QSharedPointer<TConverter> get(int metaTypeId) const;
+		void add(int metaTypeId, const QSharedPointer<TConverter> &converter);
+
+		void clear();
+
+	private:
+		mutable QReadWriteLock _lock {};
+		QHash<int, QSharedPointer<TConverter>> _store;
+	};
+
+	template <typename TConverter>
+	struct ConverterStore {
+		mutable QReadWriteLock lock {};
+		QList<QSharedPointer<TConverter>> store;
+		QAtomicInt factoryOffset = 0;
+
+		ConverterStore() = default;
+		ConverterStore(std::initializer_list<QSharedPointer<TConverter>> initData);
+
+		void insertSorted(const QSharedPointer<TConverter> &converter);
+		void insertSorted(const QSharedPointer<TConverter> &converter, QWriteLocker &locker);
+	};
+
+	static ThreadSafeStore<TypeExtractor> extractors;
+
+	static QReadWriteLock typeConverterFactoryLock;
+	static QList<TypeConverterFactory*> typeConverterFactories;
+
+	bool allowNull = false;
+	bool keepObjectName = false;
+	bool enumAsString = false;
+	bool versionAsString = false;
+	bool dateAsTimeStamp = false;
+	bool useBcp47Locale = true;
+	ValidationFlags validationFlags = ValidationFlag::StandardValidation;
+	Polymorphing polymorphing = Polymorphing::Enabled;
+	MultiMapMode multiMapMode = MultiMapMode::Map;
+	bool ignoreStoredAttribute = false;
+
+	mutable ConverterStore<TypeConverter> typeConverters;
+	mutable ThreadSafeStore<TypeConverter> serCache;
+	mutable ThreadSafeStore<TypeConverter> deserCache;
+
+	template <typename TConverter>
+	void insertSorted(const QSharedPointer<TConverter> &converter, QList<QSharedPointer<TConverter>> &list) const;
+
+	QSharedPointer<TypeConverter> findSerConverter(int propertyType) const;
+	QSharedPointer<TypeConverter> findDeserConverter(int &propertyType, QCborTag tag, QCborValue::Type type) const;
+	void updateConverterStore() const;
+
+	int getEnumId(QMetaEnum metaEnum, bool ser) const;
+	virtual QCborValue serializeValue(int propertyType, const QVariant &value) const;
+	virtual QVariant deserializeCborValue(int propertyType, const QCborValue &value) const;
+	virtual QVariant deserializeJsonValue(int propertyType, const QCborValue &value) const;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(logSerializer)
+Q_DECLARE_LOGGING_CATEGORY(logSerializerExtractor)
+
+template<typename TConverter>
+SerializerBasePrivate::ThreadSafeStore<TConverter>::ThreadSafeStore(std::initializer_list<std::pair<int, QSharedPointer<TConverter>>> initData)
+	: _store{std::move(initData)}
+{}
+
+template<typename TConverter>
+QSharedPointer<TConverter> SerializerBasePrivate::ThreadSafeStore<TConverter>::get(int metaTypeId) const
+{
+	QReadLocker _{&_lock};
+	return _store.value(metaTypeId, nullptr);
+}
+
+template<typename TConverter>
+void SerializerBasePrivate::ThreadSafeStore<TConverter>::add(int metaTypeId, const QSharedPointer<TConverter> &converter)
+{
+	QWriteLocker _{&_lock};
+	_store.insert(metaTypeId, converter);
+}
+
+template<typename TConverter>
+void SerializerBasePrivate::ThreadSafeStore<TConverter>::clear()
+{
+	QWriteLocker _{&_lock};
+	_store.clear();
+}
+
+template<typename TConverter>
+SerializerBasePrivate::ConverterStore<TConverter>::ConverterStore(std::initializer_list<QSharedPointer<TConverter>> initData)
+	: store{std::move(initData)}
+{
+#ifndef QT_NO_DEBUG
+	for (auto i = 1; i < store.size(); ++i)
+		Q_ASSERT(store[i]->priority() <= store[i - 1]->priority());
+#endif
+}
+
+template<typename TConverter>
+void SerializerBasePrivate::ConverterStore<TConverter>::insertSorted(const QSharedPointer<TConverter> &converter)
+{
+	QWriteLocker _{&lock};
+	insertSorted(converter, _);
+}
+
+template<typename TConverter>
+void SerializerBasePrivate::ConverterStore<TConverter>::insertSorted(const QSharedPointer<TConverter> &converter, QWriteLocker &locker)
+{
+	Q_UNUSED(locker)
+	for (auto it = store.begin(); it != store.end(); ++it) {
+		if ((*it)->priority() < converter->priority()) {
+			store.insert(it, converter);
+			return;
+		}
+	}
+	// not inserted -> add to end
+	store.append(converter);
+}
+
+}
+
+#endif // QTJSONSERIALIZER_SERIALIZERBASE_P_H

+ 167 - 0
jsonserializer/typeconverter.cpp

@@ -0,0 +1,167 @@
+#include "typeconverter.h"
+#include "serializerbase_p.h"
+using namespace QtJsonSerializer;
+
+namespace QtJsonSerializer {
+
+class TypeConverterPrivate
+{
+public:
+	int priority = TypeConverter::Standard;
+	const TypeConverter::SerializationHelper *helper = nullptr;
+};
+
+}
+
+
+
+TypeConverter::TypeConverter() :
+	d{new TypeConverterPrivate{}}
+{}
+
+TypeConverter::~TypeConverter() = default;
+
+int TypeConverter::priority() const
+{
+	return d->priority;
+}
+
+void TypeConverter::setPriority(int priority)
+{
+	d->priority = priority;
+}
+
+const TypeConverter::SerializationHelper *TypeConverter::helper() const
+{
+	return d->helper;
+}
+
+void TypeConverter::setHelper(const TypeConverter::SerializationHelper *helper)
+{
+	d->helper = helper;
+}
+
+QList<QCborTag> TypeConverter::allowedCborTags(int metaTypeId) const
+{
+	Q_UNUSED(metaTypeId)
+	return {};
+}
+
+int TypeConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
+{
+	Q_UNUSED(tag)
+	Q_UNUSED(dataType)
+	return QMetaType::UnknownType;
+}
+
+TypeConverter::DeserializationCapabilityResult TypeConverter::canDeserialize(int &metaTypeId, QCborTag tag, QCborValue::Type dataType) const
+{
+	const auto asJson = helper()->jsonMode();
+	const auto strict = helper()->getProperty("validationFlags")
+							.value<SerializerBase::ValidationFlags>()
+							.testFlag(SerializerBase::ValidationFlag::StrictBasicTypes);
+
+	// case A: a metaTypeId is present
+	if (metaTypeId != QMetaType::UnknownType) {
+		// first: verify the given metatype is supported
+		if (!canConvert(metaTypeId))
+			return DeserializationCapabilityResult::Negative;
+
+		// second: verify the tag if not in json mode
+		if (!asJson) {
+			// if either we are in strict mode or a tag is given, the tag is verified
+			if (strict || tag != NoTag) {
+				// If there is a list of allowed tags, the given tag must be in it
+				auto aTags = allowedCborTags(metaTypeId);
+				// also, add the type specific override tag if set
+				if (const auto xTag = helper()->typeTag(metaTypeId); xTag != NoTag)
+					aTags.append(xTag);
+				if (!aTags.isEmpty()) {
+					if (!aTags.contains(tag))
+						return DeserializationCapabilityResult::WrongTag;
+				// otherwise, if in strict mode, an empty allowed tag list means the tag must not be set
+				} else if (strict && tag != NoTag)
+					return DeserializationCapabilityResult::WrongTag;
+			}
+		}
+
+		// third: verify the datatype, based on type and tag
+		auto aTypes = allowedCborTypes(metaTypeId, tag);
+		// if in json mode, convert the supported types to their json equivalent
+		if (asJson)
+			mapTypesToJson(aTypes);
+		// then verify them
+		if (!aTypes.contains(dataType))
+			return DeserializationCapabilityResult::Negative;
+
+		return DeserializationCapabilityResult::Positive;
+	// case B: no metaTypeId is present, we are in cbor mode and have a tag
+	} else if (!asJson && tag != NoTag){
+		// try to guess the id from tag and type
+		metaTypeId = guessType(tag, dataType);
+		if (metaTypeId != QMetaType::UnknownType)
+			return DeserializationCapabilityResult::Guessed;
+		else
+			return DeserializationCapabilityResult::Negative;
+	// otherwise: cannot convert
+	} else
+		return DeserializationCapabilityResult::Negative;
+}
+
+QVariant TypeConverter::deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	return deserializeCbor(propertyType, value, parent);
+}
+
+void TypeConverter::mapTypesToJson(QList<QCborValue::Type> &typeList) const
+{
+	auto hasDouble = false;
+	auto hasInt = false;
+	for (auto &type : typeList) {
+		switch (type) {
+		case QCborValue::Double:
+			hasDouble = true;
+			break;
+		case QCborValue::Integer:
+			hasInt = true;
+			break;
+		case QCborValue::ByteArray:
+		case QCborValue::DateTime:
+		case QCborValue::Url:
+		case QCborValue::RegularExpression:
+		case QCborValue::Uuid:
+			type = QCborValue::String;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (hasInt && !hasDouble)
+		typeList.append(QCborValue::Double);
+	if (hasDouble && !hasInt)
+		typeList.append(QCborValue::Integer);
+}
+
+
+
+TypeConverter::SerializationHelper::SerializationHelper() = default;
+
+TypeConverter::SerializationHelper::~SerializationHelper() = default;
+
+
+
+TypeConverterFactory::TypeConverterFactory() = default;
+
+TypeConverterFactory::~TypeConverterFactory() = default;
+
+
+
+TypeExtractor::TypeExtractor() = default;
+
+TypeExtractor::~TypeExtractor() = default;
+
+QList<int> TypeExtractor::subtypes() const
+{
+	return {};
+}

+ 176 - 0
jsonserializer/typeconverter.h

@@ -0,0 +1,176 @@
+#ifndef QTJSONSERIALIZER_TYPECONVERTER_H
+#define QTJSONSERIALIZER_TYPECONVERTER_H
+
+#include "qtjsonserializer_global.h"
+
+#include <type_traits>
+#include <limits>
+
+#include <QtCore/qmetatype.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qjsonvalue.h>
+#include <QtCore/qcborvalue.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qsharedpointer.h>
+
+namespace QtJsonSerializer {
+
+//! Interface to extract data from any generic container
+class Q_JSONSERIALIZER_EXPORT TypeExtractor
+{
+	Q_DISABLE_COPY(TypeExtractor)
+
+public:
+	TypeExtractor();
+	virtual ~TypeExtractor();
+
+	//! Returns the identifier of the general type this extractor is for
+	virtual QByteArray baseType() const = 0;
+	//! Returns an ordered list of subtypes found in types handled by this extractor
+	virtual QList<int> subtypes() const = 0;
+	//! Extracts the data of a value of the extractors type at the given index
+	virtual QVariant extract(const QVariant &value, int index = -1) const = 0;
+	//! Emplaces the value into the target of the extractors type at the given index
+	virtual void emplace(QVariant &target, const QVariant &value, int index = -1) const = 0;
+};
+
+class TypeConverterPrivate;
+//! An interface to create custom serializer type converters
+class Q_JSONSERIALIZER_EXPORT TypeConverter
+{
+	Q_DISABLE_COPY(TypeConverter)
+public:
+	//! A placeholder tag to be used if no tag is expected/allowed/given
+	static constexpr auto NoTag = static_cast<QCborTag>(std::numeric_limits<std::underlying_type_t<QCborTag>>::max());
+
+	//! Sample values for a priority value (default converters are mostly Standard and are guaranteed to be between Low and High)
+	enum Priority : int {
+		ExtremlyLow = -0x00FFFFFF,
+		VeryLow = -0x0000FFFF,
+		Low = -0x000000FF,
+		Standard = 0x00000000,
+		High = 0x000000FF,
+		VeryHigh = 0x0000FFFF,
+		ExtremlyHigh = 0x00FFFFFF
+	};
+
+	//! The possible results of canDeserialize(), used internally only
+	enum DeserializationCapabilityResult : int {
+		Positive = 1, //!< The converter can deserialize the given data
+		Guessed = 2, //!< The converter guessed a type for the given data
+		Negative = -1, //!< The converter cannot deserialize the given data
+		WrongTag = -2 //!< The converter could deserialize the given data, but the tag does not match
+	};
+
+	//! Helper class passed to the type converter by the serializer. Do not implement yourself
+	class Q_JSONSERIALIZER_EXPORT SerializationHelper
+	{
+		Q_DISABLE_COPY(SerializationHelper)
+	public:
+		SerializationHelper();
+		virtual ~SerializationHelper();
+
+		//! Returns true, if de/serializing to JSON, and false for CBOR
+		virtual bool jsonMode() const = 0;
+		//! Returns a property from the serializer
+		virtual QVariant getProperty(const char *name) const = 0;
+		//! Returns a tag registered for the given metaTypeId
+		virtual QCborTag typeTag(int metaTypeId) const = 0;
+		//! Returns a reference to an extractor for the given type, or nullptr
+		virtual QSharedPointer<const TypeExtractor> extractor(int metaTypeId) const = 0;
+
+		//! Serialize a subvalue, represented by a meta property
+		virtual QCborValue serializeSubtype(const QMetaProperty &property, const QVariant &value) const = 0;
+		//! Serialize a subvalue, represented by a type id
+		virtual QCborValue serializeSubtype(int propertyType, const QVariant &value, const QByteArray &traceHint = {}) const = 0;
+		//! Deserialize a subvalue, represented by a meta property
+		virtual QVariant deserializeSubtype(const QMetaProperty &property, const QCborValue &value, QObject *parent) const = 0;
+		//! Deserialize a subvalue, represented by a type id
+		virtual QVariant deserializeSubtype(int propertyType, const QCborValue &value, QObject *parent, const QByteArray &traceHint = {}) const = 0;
+	};
+
+	//! Constructor
+	TypeConverter();
+	//! Destructor
+	virtual ~TypeConverter();
+
+	//! The name of the converter. Used for debugging purpose
+	virtual QByteArray name() const = 0;
+
+	//! Returns the priority of this converter
+	int priority() const;
+	//! Sets the priority of this converter
+	void setPriority(int priority);
+
+	//! Returns the helper associated with this converter
+	const SerializationHelper *helper() const;
+	//! @private
+	void setHelper(const SerializationHelper *helper);
+
+	//! Returns true, if this implementation can convert the given type
+	virtual bool canConvert(int metaTypeId) const = 0;
+	//! Returns a list of allowed tags for the given type
+	virtual QList<QCborTag> allowedCborTags(int metaTypeId) const;
+	//! Returns a list of allowed types for the given type and tag
+	virtual QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const = 0;
+	//! Returns a type guessed from the tag and data, that is supported by the converter
+	virtual int guessType(QCborTag tag, QCborValue::Type dataType) const;
+	//! @private
+	DeserializationCapabilityResult canDeserialize(int &metaTypeId,
+												   QCborTag tag,
+												   QCborValue::Type dataType) const;
+
+	//! Called by the serializer to serializer your given type to CBOR
+	virtual QCborValue serialize(int propertyType, const QVariant &value) const = 0;
+	//! Called by the serializer to deserializer your given type from CBOR
+	virtual QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const = 0;
+	//! Called by the serializer to deserializer your given type from JSON
+	virtual QVariant deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const;
+
+private:
+	QScopedPointer<TypeConverterPrivate> d;
+
+	void mapTypesToJson(QList<QCborValue::Type> &typeList) const;
+};
+
+//! Macro to implement the TypeConverter::name method in a subclass
+#define QT_JSONSERIALIZER_TYPECONVERTER_NAME(className) inline QByteArray name() const override { \
+	static_assert(std::is_same_v<className, std::decay_t<decltype(*this)>>); \
+	return QByteArrayLiteral(#className); \
+}
+
+//! A factory interface to create instances of QJsonTypeConverters
+class Q_JSONSERIALIZER_EXPORT TypeConverterFactory
+{
+	Q_DISABLE_COPY(TypeConverterFactory)
+
+public:
+	TypeConverterFactory();
+	virtual ~TypeConverterFactory();
+
+	//! The primary factory method to create converters
+	virtual QSharedPointer<TypeConverter> createConverter() const = 0;
+};
+
+//! A template implementation of TypeConverterFactory to generically create simply converters
+template <typename TConverter, int OverwritePriority = TypeConverter::Priority::Standard>
+class TypeConverterStandardFactory : public TypeConverterFactory
+{
+public:
+	QSharedPointer<TypeConverter> createConverter() const override;
+};
+
+// ------------- GENERIC IMPLEMENTATION -------------
+
+template<typename TConverter, int OverwritePriority>
+QSharedPointer<TypeConverter> TypeConverterStandardFactory<TConverter, OverwritePriority>::createConverter() const
+{
+	auto converter = QSharedPointer<TConverter>::create();
+	if (OverwritePriority != TypeConverter::Priority::Standard)
+		converter->setPriority(OverwritePriority);
+	return converter;
+}
+
+}
+
+#endif // QTJSONSERIALIZER_TYPECONVERTER_H

+ 74 - 0
jsonserializer/typeconverters/bitarrayconverter.cpp

@@ -0,0 +1,74 @@
+#include "bitarrayconverter_p.h"
+#include "cborserializer.h"
+#include <QtCore/QBitArray>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+bool BitArrayConverter::canConvert(int metaTypeId) const
+{
+	return metaTypeId == QMetaType::QBitArray;
+}
+
+QList<QCborTag> BitArrayConverter::allowedCborTags(int metaTypeId) const
+{
+	Q_UNUSED(metaTypeId)
+	return {static_cast<QCborTag>(CborSerializer::BitArray)};
+}
+
+QList<QCborValue::Type> BitArrayConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::ByteArray};
+}
+
+int BitArrayConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
+{
+	if (tag == static_cast<QCborTag>(CborSerializer::BitArray) &&
+		dataType == QCborValue::ByteArray)
+		return QMetaType::QBitArray;
+	else
+		return QMetaType::UnknownType;
+}
+
+QCborValue BitArrayConverter::serialize(int propertyType, const QVariant &value) const
+{
+	Q_UNUSED(propertyType)
+	const auto bitArray = value.value<QBitArray>();
+	if (bitArray.isEmpty())
+		return {static_cast<QCborTag>(CborSerializer::BitArray), QByteArray{}};
+	else {
+		const auto byteLen = bitArray.size() % 8 == 0 ?
+													  bitArray.size() / 8 :
+													  (bitArray.size() / 8) + 1;
+		QByteArray cData(byteLen + 1, 0);
+		cData[0] = static_cast<char>(bitArray.size() % 8);
+		memcpy(cData.data() + 1, bitArray.bits(), static_cast<size_t>(byteLen));
+		return {static_cast<QCborTag>(CborSerializer::BitArray), cData};
+	}
+}
+
+QVariant BitArrayConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(propertyType)
+	Q_UNUSED(parent)
+
+	const auto cData = (value.isTag() ? value.taggedValue() : value).toByteArray();
+	if (cData.isEmpty())
+		return QBitArray{};
+	else {
+		const auto byteLen = cData.size() - 1;
+		const auto sSize = static_cast<int>(cData[0]);
+		if (sSize == 0)
+			return QBitArray::fromBits(cData.data() + 1, byteLen * 8);
+		else
+			return QBitArray::fromBits(cData.data() + 1, (byteLen - 1) * 8 + sSize);
+	}
+}
+
+QVariant BitArrayConverter::deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	return deserializeCbor(propertyType,
+						   QByteArray::fromBase64(value.toString().toUtf8(), QByteArray::Base64UrlEncoding),
+						   parent);
+}

+ 24 - 0
jsonserializer/typeconverters/bitarrayconverter_p.h

@@ -0,0 +1,24 @@
+#ifndef QTJSONSERIALIZER_BITARRAYCONVERTER_H
+#define QTJSONSERIALIZER_BITARRAYCONVERTER_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT BitArrayConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(BitArrayConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	int guessType(QCborTag tag, QCborValue::Type dataType) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+	QVariant deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_BITARRAYCONVERTER_H

+ 105 - 0
jsonserializer/typeconverters/bytearrayconverter.cpp

@@ -0,0 +1,105 @@
+#include "bytearrayconverter_p.h"
+#include "exception.h"
+#include "jsonserializer.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QRegularExpression>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+bool BytearrayConverter::canConvert(int metaTypeId) const
+{
+	return metaTypeId == QMetaType::QByteArray;
+}
+
+QList<QCborTag> BytearrayConverter::allowedCborTags(int metaTypeId) const
+{
+	Q_UNUSED(metaTypeId)
+	return {
+		NoTag,
+		static_cast<QCborTag>(QCborKnownTags::ExpectedBase64),
+		static_cast<QCborTag>(QCborKnownTags::ExpectedBase64url),
+		static_cast<QCborTag>(QCborKnownTags::ExpectedBase16),
+	};
+}
+
+QList<QCborValue::Type> BytearrayConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::ByteArray};
+}
+
+int BytearrayConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
+{
+	Q_UNUSED(dataType)
+	switch (tag) {
+	case static_cast<QCborTag>(QCborKnownTags::ExpectedBase64):
+	case static_cast<QCborTag>(QCborKnownTags::ExpectedBase64url):
+	case static_cast<QCborTag>(QCborKnownTags::ExpectedBase16):
+		return QMetaType::QByteArray;
+	default:
+		return QMetaType::UnknownType;
+	}
+}
+
+QCborValue BytearrayConverter::serialize(int propertyType, const QVariant &value) const
+{
+	Q_UNUSED(propertyType)
+	return value.toByteArray();
+}
+
+QVariant BytearrayConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(propertyType)
+	Q_UNUSED(parent)
+	return (value.isTag() ? value.taggedValue() : value).toByteArray();
+}
+
+QVariant BytearrayConverter::deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(propertyType)
+	Q_UNUSED(parent)
+
+	const auto mode = helper()->getProperty("byteArrayFormat").value<JsonSerializer::ByteArrayFormat>();
+	const auto strValue = value.toString();
+	if (helper()->getProperty("validateBase64").toBool()) {
+		switch (mode) {
+		case JsonSerializer::ByteArrayFormat::Base64: {
+			if ((strValue.size() % 4) != 0)
+				throw DeserializationException("String has invalid length for base64 encoding");
+			static const QRegularExpression regex(QStringLiteral(R"__(^[a-zA-Z0-9+\/]*(={0,2})$)__"));
+			if (!regex.match(strValue).hasMatch())
+				throw DeserializationException("String contains unallowed symbols for base64 encoding");
+			break;
+		}
+		case JsonSerializer::ByteArrayFormat::Base64url: {
+			static const QRegularExpression regex(QStringLiteral(R"__(^[a-zA-Z0-9\-_]*$)__"));
+			if (!regex.match(strValue).hasMatch())
+				throw DeserializationException("String contains unallowed symbols for base64url encoding");
+			break;
+		}
+		case JsonSerializer::ByteArrayFormat::Base16: {
+			if ((strValue.size() % 2) != 0)
+				throw DeserializationException("String has invalid length for base16 encoding");
+			static const QRegularExpression regex(QStringLiteral(R"__(^[a-fA-F0-9]*$)__"));
+			if (!regex.match(strValue).hasMatch())
+				throw DeserializationException("String contains unallowed symbols for base16 encoding");
+			break;
+		}
+		default:
+			Q_UNREACHABLE();
+		}
+	}
+
+	switch (mode) {
+	case JsonSerializer::ByteArrayFormat::Base64:
+		return QByteArray::fromBase64(strValue.toUtf8(), QByteArray::Base64Encoding);
+	case JsonSerializer::ByteArrayFormat::Base64url:
+		return QByteArray::fromBase64(strValue.toUtf8(), QByteArray::Base64UrlEncoding);
+	case JsonSerializer::ByteArrayFormat::Base16:
+		return QByteArray::fromHex(strValue.toUtf8());
+	default:
+		Q_UNREACHABLE();
+	}
+}

+ 24 - 0
jsonserializer/typeconverters/bytearrayconverter_p.h

@@ -0,0 +1,24 @@
+#ifndef QTJSONSERIALIZER_BYTEARRAYCONVERTER_P_H
+#define QTJSONSERIALIZER_BYTEARRAYCONVERTER_P_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT BytearrayConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(BytearrayConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	int guessType(QCborTag tag, QCborValue::Type dataType) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+	QVariant deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_BYTEARRAYCONVERTER_P_H

+ 118 - 0
jsonserializer/typeconverters/cborconverter.cpp

@@ -0,0 +1,118 @@
+#include "cborconverter_p.h"
+#include "exception.h"
+
+#include <QtCore/QSet>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonDocument>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+bool CborConverter::canConvert(int metaTypeId) const
+{
+	static const QSet<int> metaTypes {
+		QMetaType::QCborValue,
+		QMetaType::QCborMap,
+		QMetaType::QCborArray,
+		QMetaType::QCborSimpleType,
+		QMetaType::QJsonValue,
+		QMetaType::QJsonObject,
+		QMetaType::QJsonArray,
+		QMetaType::QJsonDocument,
+	};
+	return metaTypes.contains(metaTypeId);
+}
+
+QList<QCborValue::Type> CborConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(tag)
+	switch (metaTypeId) {
+	case QMetaType::QCborValue:{
+		QList<QCborValue::Type> types;
+		const auto metaEnum = QMetaEnum::fromType<QCborValue::Type>();
+		types.reserve(metaEnum.keyCount());
+		for (auto i = 0; i < metaEnum.keyCount(); ++i) {
+			if (const auto value = metaEnum.value(i); value != QCborValue::Invalid)
+				types.append(static_cast<QCborValue::Type>(value));
+		}
+		return types;
+	}
+	case QMetaType::QJsonValue:
+		return {
+			QCborValue::Null,
+			QCborValue::True,
+			QCborValue::False,
+			QCborValue::Double,
+			QCborValue::String,
+			QCborValue::Array,
+			QCborValue::Map
+		};
+	case QMetaType::QCborSimpleType:
+		return {
+			QCborValue::SimpleType,
+			QCborValue::True,
+			QCborValue::False,
+			QCborValue::Null,
+			QCborValue::Undefined
+		};
+	case QMetaType::QCborMap:
+	case QMetaType::QJsonObject:
+		return {QCborValue::Map};
+	case QMetaType::QCborArray:
+	case QMetaType::QJsonArray:
+		return {QCborValue::Array};
+	case QMetaType::QJsonDocument:
+		return {QCborValue::Map, QCborValue::Array, QCborValue::Null};
+	default:
+		Q_UNREACHABLE();
+	}
+}
+
+QCborValue CborConverter::serialize(int propertyType, const QVariant &value) const
+{
+	switch (propertyType) {
+	case QMetaType::QCborValue:
+	case QMetaType::QCborSimpleType:
+	case QMetaType::QCborMap:
+	case QMetaType::QCborArray:
+		return value.value<QCborValue>();
+	case QMetaType::QJsonValue:
+	case QMetaType::QJsonObject:
+	case QMetaType::QJsonArray:
+	case QMetaType::QJsonDocument:
+		return QCborValue::fromJsonValue(value.toJsonValue());
+	default:
+		throw SerializationException{"Unsupported type"};
+	}
+}
+
+QVariant CborConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(parent)
+	switch (propertyType) {
+	case QMetaType::QJsonValue:
+		return QVariant::fromValue(value.toJsonValue());
+	case QMetaType::QJsonObject:
+		return QVariant::fromValue(value.toJsonValue().toObject());
+	case QMetaType::QJsonArray:
+		return QVariant::fromValue(value.toJsonValue().toArray());
+	case QMetaType::QJsonDocument: {
+		const auto jValue = value.toJsonValue();
+		switch (jValue.type()) {
+		case QJsonValue::Array:
+			return QVariant::fromValue(QJsonDocument{jValue.toArray()});
+		case QJsonValue::Object:
+			return QVariant::fromValue(QJsonDocument{jValue.toObject()});
+		default:
+			return QVariant::fromValue(QJsonDocument{});
+		}
+	}
+	case QMetaType::QCborValue:
+	case QMetaType::QCborSimpleType:
+	case QMetaType::QCborMap:
+	case QMetaType::QCborArray:
+		return QVariant::fromValue(value);
+	default:
+		throw DeserializationException{"Unsupported type"};
+	}
+}

+ 22 - 0
jsonserializer/typeconverters/cborconverter_p.h

@@ -0,0 +1,22 @@
+#ifndef QTJSONSERIALIZER_CBORCONVERTER_H
+#define QTJSONSERIALIZER_CBORCONVERTER_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT CborConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(CborConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_CBORCONVERTER_H
+

+ 125 - 0
jsonserializer/typeconverters/datetimeconverter.cpp

@@ -0,0 +1,125 @@
+#include "datetimeconverter_p.h"
+#include "exception.h"
+#include "cborserializer.h"
+#include <QtCore/QSet>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+bool DateTimeConverter::canConvert(int metaTypeId) const
+{
+	static const QSet<int> types {
+		QMetaType::QDateTime,
+		QMetaType::QDate,
+		QMetaType::QTime
+	};
+	return types.contains(metaTypeId);
+}
+
+QList<QCborTag> DateTimeConverter::allowedCborTags(int metaTypeId) const
+{
+	switch (metaTypeId) {
+	case QMetaType::QDateTime:
+		return {static_cast<QCborTag>(QCborKnownTags::DateTimeString), static_cast<QCborTag>(QCborKnownTags::UnixTime_t)};
+	case QMetaType::QDate:
+		return {static_cast<QCborTag>(QCborKnownTags::DateTimeString), static_cast<QCborTag>(CborSerializer::Date)};
+	case QMetaType::QTime:
+		return {static_cast<QCborTag>(QCborKnownTags::DateTimeString), static_cast<QCborTag>(CborSerializer::Time)};
+	default:
+		return {};
+	}
+}
+
+QList<QCborValue::Type> DateTimeConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	switch (tag) {
+	case static_cast<QCborTag>(QCborKnownTags::UnixTime_t):
+		return {QCborValue::Integer};
+	case static_cast<QCborTag>(QCborKnownTags::DateTimeString):
+	case static_cast<QCborTag>(CborSerializer::Date):
+	case static_cast<QCborTag>(CborSerializer::Time):
+		return {QCborValue::String};
+	default:
+		if (metaTypeId == QMetaType::QDateTime)
+			return {QCborValue::String, QCborValue::Integer};
+		else
+			return {QCborValue::String};
+	}
+}
+
+int DateTimeConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
+{
+	switch (tag) {
+	case static_cast<QCborTag>(QCborKnownTags::DateTimeString):
+		if (dataType == QCborValue::String)
+			return QMetaType::QDateTime;
+		else
+			break;
+	case static_cast<QCborTag>(QCborKnownTags::UnixTime_t):
+		if (dataType == QCborValue::Integer)
+			return QMetaType::QDateTime;
+		else
+			break;
+	case static_cast<QCborTag>(CborSerializer::Date):
+		if (dataType == QCborValue::String)
+			return QMetaType::QDate;
+		else
+			break;
+	case static_cast<QCborTag>(CborSerializer::Time):
+		if (dataType == QCborValue::String)
+			return QMetaType::QTime;
+		else
+			break;
+	default:
+		break;
+	}
+
+	return QMetaType::UnknownType;
+}
+
+QCborValue DateTimeConverter::serialize(int propertyType, const QVariant &value) const
+{
+	switch (propertyType) {
+	case QMetaType::QDateTime:
+		if (helper()->getProperty("dateAsTimeStamp").toBool())
+			return {QCborKnownTags::UnixTime_t, value.toDateTime().toUTC().toSecsSinceEpoch()};
+		else
+			return QCborValue{value.toDateTime()};
+	case QMetaType::QDate:
+		return {static_cast<QCborTag>(CborSerializer::Date), value.toDate().toString(Qt::ISODate)};
+	case QMetaType::QTime:
+		return {static_cast<QCborTag>(CborSerializer::Time), value.toTime().toString(Qt::ISODateWithMs)};
+	default:
+		throw SerializationException{"Invalid property type"};
+	}
+}
+
+QVariant DateTimeConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(parent)
+	const auto cValue = (value.isTag() ? value.taggedValue() : value);
+	switch (propertyType) {
+	case QMetaType::QDateTime:
+		return value.toDateTime();
+	case QMetaType::QDate:
+		if (value.tag() == QCborKnownTags::DateTimeString)
+			return value.toDateTime().date();
+		else
+			return QDate::fromString(cValue.toString(), Qt::ISODate);
+	case QMetaType::QTime:
+		if (value.tag() == QCborKnownTags::DateTimeString)
+			return value.toDateTime().time();
+		else
+			return QTime::fromString(cValue.toString(), Qt::ISODateWithMs);
+	default:
+		throw SerializationException{"Invalid property type"};
+	}
+}
+
+QVariant DateTimeConverter::deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	if (propertyType == QMetaType::QDateTime &&
+		value.type() == QCborValue::String)
+		return deserializeCbor(propertyType, {QCborKnownTags::DateTimeString, value}, parent);
+	else
+		return deserializeCbor(propertyType, value, parent);
+}

+ 26 - 0
jsonserializer/typeconverters/datetimeconverter_p.h

@@ -0,0 +1,26 @@
+#ifndef QTJSONSERIALIZER_DATETIMECONVERTER_H
+#define QTJSONSERIALIZER_DATETIMECONVERTER_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+#include <QtCore/QDateTime>
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT DateTimeConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(DateTimeConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	int guessType(QCborTag tag, QCborValue::Type dataType) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+	QVariant deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_DATETIMECONVERTER_H

+ 143 - 0
jsonserializer/typeconverters/enumconverter.cpp

@@ -0,0 +1,143 @@
+#include "enumconverter_p.h"
+#include "exception.h"
+#include "cborserializer.h"
+
+#include <cmath>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+namespace {
+// Q_NORETURN inline void throwSer(QByteArray &&what, bool ser)
+// {
+// 	if (ser)
+// 		throw SerializationException{std::move(what)};
+// 	else
+// 		throw DeserializationException{std::move(what)};
+// }
+Q_NORETURN inline void throwSer(const QByteArray &what, bool ser)
+{
+    if (ser)
+        throw SerializationException{std::move(what)};
+    else
+        throw DeserializationException{std::move(what)};
+}
+}
+
+EnumConverter::EnumConverter()
+{
+	setPriority(TypeConverter::Low);
+}
+
+bool EnumConverter::canConvert(int metaTypeId) const
+{
+	return QMetaType(metaTypeId).flags().testFlag(QMetaType::IsEnumeration) ||
+			testForEnum(metaTypeId);  // NOTE check once in a while if still needed
+}
+
+QList<QCborTag> EnumConverter::allowedCborTags(int metaTypeId) const
+{
+	const auto metaEnum = getEnum(metaTypeId, false);
+	if (metaEnum.isFlag())
+		return {static_cast<QCborTag>(CborSerializer::Flags)};
+	else
+		return {static_cast<QCborTag>(CborSerializer::Enum)};
+}
+
+QList<QCborValue::Type> EnumConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::Integer, QCborValue::String};
+}
+
+QCborValue EnumConverter::serialize(int propertyType, const QVariant &value) const
+{
+	const auto metaEnum = getEnum(propertyType, true);
+	const auto tag = static_cast<QCborTag>(metaEnum.isFlag() ? CborSerializer::Flags : CborSerializer::Enum);
+	if (helper()->getProperty("enumAsString").toBool()) {
+		if (metaEnum.isFlag())
+			return {tag, QString::fromUtf8(metaEnum.valueToKeys(value.toInt()))};
+		else
+			return {tag, QString::fromUtf8(metaEnum.valueToKey(value.toInt()))};
+	} else
+		return {tag, value.toInt()};
+}
+
+QVariant EnumConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(parent)
+	const auto metaEnum = getEnum(propertyType, false);
+	auto cValue = value.isTag() ? value.taggedValue() : value;
+	if (cValue.isString()) {
+		auto result = -1;
+		auto ok = false;
+		if (metaEnum.isFlag())
+			result = metaEnum.keysToValue(qUtf8Printable(cValue.toString()), &ok);
+		else
+			result = metaEnum.keyToValue(qUtf8Printable(cValue.toString()), &ok);
+		if (ok)
+			return result;
+		else if(metaEnum.isFlag() && cValue.toString().isEmpty())
+			return 0;
+		else {
+			throw DeserializationException{QByteArray{"Invalid value for enum type \""} +
+												metaEnum.name() +
+												"\": " +
+												cValue.toString().toUtf8()};
+		}
+	} else {
+		const auto intValue = cValue.toInteger();
+		if (!metaEnum.isFlag() && metaEnum.valueToKey(intValue) == nullptr) {
+			throw DeserializationException{"Invalid integer value. Not a valid enum/flags element: " +
+												QByteArray::number(intValue)};
+		}
+		return static_cast<int>(intValue);
+	}
+}
+
+QVariant EnumConverter::deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	if (value.isDouble()) {
+		double intpart;
+		if (std::modf(value.toDouble(), &intpart) != 0.0) {
+			throw DeserializationException{"Invalid value (double) for enum type found: " +
+												QByteArray::number(value.toDouble())};
+		}
+	}
+	return deserializeCbor(propertyType, value, parent);
+}
+
+bool EnumConverter::testForEnum(int metaTypeId) const
+{
+	try {
+		getEnum(metaTypeId, true);
+		return true;
+	} catch (Exception &) {
+		return false;
+	}
+}
+
+QMetaEnum EnumConverter::getEnum(int metaTypeId, bool ser) const
+{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	const auto mo = QMetaType::metaObjectForType(metaTypeId);
+#else
+	const auto mo = QMetaType(metaTypeId).metaObject();
+#endif
+	if (!mo)
+		throwSer(QByteArray{"Unable to get metaobject for type "} + QMetaTypeName(metaTypeId), ser);
+	const auto enumName = QString::fromUtf8(QMetaTypeName(metaTypeId))
+							  .split(QStringLiteral("::"))
+							  .last()
+							  .toUtf8();
+	auto mIndex = mo->indexOfEnumerator(enumName.data());
+	if (mIndex < 0) {
+		throwSer(QByteArray{"Unable to get QMetaEnum for type "} +
+					 QMetaTypeName(metaTypeId) +
+					 QByteArray{" using the owning meta object "} +
+					 mo->className(),
+				 ser);
+	}
+
+	return mo->enumerator(mIndex);
+}

+ 30 - 0
jsonserializer/typeconverters/enumconverter_p.h

@@ -0,0 +1,30 @@
+#ifndef QTJSONSERIALIZER_ENUMCONVERTER_H
+#define QTJSONSERIALIZER_ENUMCONVERTER_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+#include <QtCore/QMetaEnum>
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT EnumConverter : public TypeConverter
+{
+public:
+	EnumConverter();
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(EnumConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+	QVariant deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const override;
+
+private:
+	bool testForEnum(int metaTypeId) const;
+	QMetaEnum getEnum(int metaTypeId, bool ser) const;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_ENUMCONVERTER_H

+ 169 - 0
jsonserializer/typeconverters/gadgetconverter.cpp

@@ -0,0 +1,169 @@
+#include "gadgetconverter_p.h"
+#include "exception.h"
+#include "serializerbase_p.h"
+
+#include <QtCore/QMetaProperty>
+#include <QtCore/QSet>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+bool GadgetConverter::canConvert(int metaTypeId) const
+{
+	// exclude a few Qt gadgets that have no properties and thus need to be handled otherwise
+	static const QSet<int> gadgetExceptions {
+		QMetaType::QKeySequence,
+		QMetaType::QFont,
+		QMetaType::QLocale,
+	};
+	if(gadgetExceptions.contains(metaTypeId))
+		return false;
+
+
+	const auto flags = QMetaType(metaTypeId).flags();
+	return flags.testFlag(QMetaType::IsGadget) ||
+			flags.testFlag(QMetaType::PointerToGadget);
+}
+
+QList<QCborValue::Type> GadgetConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(tag)
+	if (QMetaType(metaTypeId).flags().testFlag(QMetaType::PointerToGadget))
+		return {QCborValue::Map, QCborValue::Null};
+	else
+		return {QCborValue::Map};
+}
+
+QCborValue GadgetConverter::serialize(int propertyType, const QVariant &value) const
+{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	const auto metaObject = QMetaType::metaObjectForType(propertyType);
+#else
+	auto metaType = QMetaType(propertyType);
+	const auto metaObject = metaType.metaObject();
+#endif
+
+	if (!metaObject)
+		throw SerializationException(QByteArray("Unable to get metaobject for type ") + QMetaTypeName(propertyType));
+	const auto isPtr = QMetaType(propertyType).flags().testFlag(QMetaType::PointerToGadget);
+
+	auto gValue = value;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	if (!gValue.convert(propertyType))
+#else
+	if (!gValue.convert(metaType))
+#endif
+		throw SerializationException(QByteArray("Data is not of the required gadget type ") + QMetaTypeName(propertyType));
+	const void *gadget = nullptr;
+	if (isPtr) {
+		// with pointers, null gadgets are allowed
+		gadget = *reinterpret_cast<const void* const *>(gValue.constData());
+		if (!gadget)
+			return QCborValue::Null;
+	} else
+		gadget = gValue.constData();
+	if (!gadget)
+		throw SerializationException(QByteArray("Unable to get address of gadget ") + QMetaTypeName(propertyType));
+
+	QCborMap cborMap;
+	//go through all properties and try to serialize them
+	const auto ignoreStoredAttribute = helper()->getProperty("ignoreStoredAttribute").toBool();
+	for (auto i = 0; i < metaObject->propertyCount(); i++) {
+		auto property = metaObject->property(i);
+		if (ignoreStoredAttribute || property.isStored())
+			cborMap[QString::fromUtf8(property.name())] = helper()->serializeSubtype(property, property.readOnGadget(gadget));
+	}
+
+	return cborMap;
+}
+
+QVariant GadgetConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(parent)  // gadgets neither have nor serve as parent
+	const auto isPtr = QMetaType(propertyType).flags().testFlag(QMetaType::PointerToGadget);
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+        const auto metaObject = QMetaType::metaObjectForType(propertyType);
+#else
+        auto metaType = QMetaType(propertyType);
+        const auto metaObject = metaType.metaObject();
+#endif
+	
+	if (!metaObject)
+		throw DeserializationException(QByteArray("Unable to get metaobject for gadget type") + QMetaTypeName(propertyType));
+
+	auto cValue = value.isTag() ? value.taggedValue() : value;
+	QVariant gadget;
+	void *gadgetPtr = nullptr;
+	if (isPtr) {
+		if (cValue.isNull())
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+			return QVariant{propertyType, nullptr};  // initialize an empty (nullptr) variant
+		const auto gadgetType = QMetaType::type(metaObject->className());
+		if (gadgetType == QMetaType::UnknownType)
+			throw DeserializationException(QByteArray("Unable to get type of gadget from gadget-pointer type") + QMetaTypeName(propertyType));
+		gadgetPtr = QMetaType::create(gadgetType);
+		gadget = QVariant{propertyType, &gadgetPtr};
+#else
+			return QVariant{metaType, nullptr};  // initialize an empty (nullptr) variant
+		auto gadgetMetaType = QMetaType::fromName(metaObject->className());
+		if (!gadgetMetaType.isValid())
+			throw DeserializationException(QByteArray("Unable to get type of gadget from gadget-pointer type") + QMetaTypeName(propertyType));
+		gadgetPtr = gadgetMetaType.create();
+		gadget = QVariant{metaType, &gadgetPtr};
+#endif
+	} else {
+		if (cValue.isNull())
+			return QVariant{};  // return to allow default null for gadgets. If not allowed, this will fail, as a null variant cannot be converted to a gadget
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+		gadget = QVariant{propertyType, nullptr};
+#else
+		gadget = QVariant{metaType, nullptr};
+#endif
+		gadgetPtr = gadget.data();
+	}
+
+	if (!gadgetPtr) {
+		throw DeserializationException(QByteArray("Failed to construct gadget of type ") +
+											QMetaTypeName(propertyType) +
+											QByteArray(". Does it have a default constructor?"));
+	}
+
+	const auto validationFlags = helper()->getProperty("validationFlags").value<SerializerBase::ValidationFlags>();
+	const auto ignoreStoredAttribute = helper()->getProperty("ignoreStoredAttribute").toBool();
+
+	// collect required properties, if set
+	QSet<QByteArray> reqProps;
+	if (validationFlags.testFlag(SerializerBase::ValidationFlag::AllProperties)) {
+		for (auto i = 0; i < metaObject->propertyCount(); i++) {
+			auto property = metaObject->property(i);
+			if (ignoreStoredAttribute || property.isStored())
+				reqProps.insert(property.name());
+		}
+	}
+
+	// now deserialize all json properties
+	const auto cborMap = cValue.toMap();
+	for (auto it = cborMap.constBegin(); it != cborMap.constEnd(); it++) {
+		const auto key = it.key().toString().toUtf8();
+		const auto propIndex = metaObject->indexOfProperty(key);
+		if (propIndex != -1) {
+			const auto property = metaObject->property(propIndex);
+			property.writeOnGadget(gadgetPtr, helper()->deserializeSubtype(property, it.value(), nullptr));
+			reqProps.remove(property.name());
+		} else if (validationFlags.testFlag(SerializerBase::ValidationFlag::NoExtraProperties)) {
+			throw DeserializationException("Found extra property " +
+												key +
+												" but extra properties are not allowed");
+		}
+	}
+
+	// make sure all required properties have been read
+	if (validationFlags.testFlag(SerializerBase::ValidationFlag::AllProperties) && !reqProps.isEmpty()) {
+		throw DeserializationException(QByteArray("Not all properties for ") +
+											metaObject->className() +
+											QByteArray(" are present in the json object. Missing properties: ") +
+											reqProps.values().join(", "));
+	}
+
+	return gadget;
+}

+ 21 - 0
jsonserializer/typeconverters/gadgetconverter_p.h

@@ -0,0 +1,21 @@
+#ifndef QTJSONSERIALIZER_GADGETCONVERTER_P_H
+#define QTJSONSERIALIZER_GADGETCONVERTER_P_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT GadgetConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(GadgetConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_GADGETCONVERTER_P_H

+ 229 - 0
jsonserializer/typeconverters/geomconverter.cpp

@@ -0,0 +1,229 @@
+#include "geomconverter_p.h"
+#include "exception.h"
+#include "cborserializer.h"
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+bool GeomConverter::canConvert(int metaTypeId) const
+{
+	static const QVector<int> types {
+		QMetaType::QSize,
+		QMetaType::QSizeF,
+		QMetaType::QPoint,
+		QMetaType::QPointF,
+		QMetaType::QLine,
+		QMetaType::QLineF,
+		QMetaType::QRect,
+		QMetaType::QRectF,
+	};
+	return types.contains(metaTypeId);
+}
+
+QList<QCborTag> GeomConverter::allowedCborTags(int metaTypeId) const
+{
+	switch (metaTypeId) {
+	case QMetaType::QSize:
+	case QMetaType::QSizeF:
+		return {static_cast<QCborTag>(CborSerializer::GeomSize)};
+	case QMetaType::QPoint:
+	case QMetaType::QPointF:
+		return {static_cast<QCborTag>(CborSerializer::GeomPoint)};
+	case QMetaType::QLine:
+	case QMetaType::QLineF:
+		return {static_cast<QCborTag>(CborSerializer::GeomLine)};
+	case QMetaType::QRect:
+	case QMetaType::QRectF:
+		return {static_cast<QCborTag>(CborSerializer::GeomRect)};
+	default:
+		return {};
+	}
+}
+
+QList<QCborValue::Type> GeomConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::Array};
+}
+
+int GeomConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
+{
+	if (dataType != QCborValue::Array)
+		return QMetaType::UnknownType;
+	switch (tag) {
+	case static_cast<QCborTag>(CborSerializer::GeomSize):
+		return QMetaType::QSizeF;
+	case static_cast<QCborTag>(CborSerializer::GeomPoint):
+		return QMetaType::QPointF;
+	case static_cast<QCborTag>(CborSerializer::GeomLine):
+		return QMetaType::QLineF;
+	case static_cast<QCborTag>(CborSerializer::GeomRect):
+		return QMetaType::QRectF;
+	default:
+		return QMetaType::UnknownType;
+	}
+}
+
+QCborValue GeomConverter::serialize(int propertyType, const QVariant &value) const
+{
+	switch (propertyType) {
+	case QMetaType::QSize:
+		return serializeSize(value.toSize());
+	case QMetaType::QSizeF:
+		return serializeSize(value.toSizeF());
+	case QMetaType::QPoint:
+		return serializePoint(value.toPoint());
+	case QMetaType::QPointF:
+		return serializePoint(value.toPointF());
+	case QMetaType::QLine:
+		return serializeLine(value.toLine());
+	case QMetaType::QLineF:
+		return serializeLine(value.toLineF());
+	case QMetaType::QRect:
+		return serializeRect(value.toRect());
+	case QMetaType::QRectF:
+		return serializeRect(value.toRectF());
+	default:
+		throw SerializationException{"Invalid type id"};
+	}
+}
+
+QVariant GeomConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(parent)
+	const auto array = (value.isTag() ? value.taggedValue() : value).toArray();
+	switch (propertyType) {
+	case QMetaType::QSize:
+		return deserializeSize<QSize>(array);
+	case QMetaType::QSizeF:
+		return deserializeSize<QSizeF>(array);
+	case QMetaType::QPoint:
+		return deserializePoint<QPoint>(array);
+	case QMetaType::QPointF:
+		return deserializePoint<QPointF>(array);
+	case QMetaType::QLine:
+		return deserializeLine<QLine>(array);
+	case QMetaType::QLineF:
+		return deserializeLine<QLineF>(array);
+	case QMetaType::QRect:
+		return deserializeRect<QRect>(array);
+	case QMetaType::QRectF:
+		return deserializeRect<QRectF>(array);
+	default:
+		throw DeserializationException{"Invalid type id"};
+	}
+}
+
+QCborValue GeomConverter::serializeSize(const std::variant<QSize, QSizeF> &size) const
+{
+	return {
+		static_cast<QCborTag>(CborSerializer::GeomSize),
+		std::visit([](const auto &s) -> QCborArray {
+			return {s.width(), s.height()};
+		}, size)
+	};
+}
+
+QCborValue GeomConverter::serializePoint(const std::variant<QPoint, QPointF> &point) const
+{
+	return {
+		static_cast<QCborTag>(CborSerializer::GeomPoint),
+		std::visit([](const auto &p) -> QCborArray {
+			return {p.x(), p.y()};
+		}, point)
+	};
+}
+
+QCborValue GeomConverter::serializeLine(const std::variant<QLine, QLineF> &line) const
+{
+	return {
+		static_cast<QCborTag>(CborSerializer::GeomLine),
+		std::visit([this](const auto &l) -> QCborArray {
+			return {
+				helper()->serializeSubtype(qMetaTypeId<std::decay_t<decltype(l.p1())>>(), l.p1(), "p1"),
+				helper()->serializeSubtype(qMetaTypeId<std::decay_t<decltype(l.p2())>>(), l.p2(), "p2")
+			};
+		}, line)
+	};
+}
+
+QCborValue GeomConverter::serializeRect(const std::variant<QRect, QRectF> &rect) const
+{
+	return {
+		static_cast<QCborTag>(CborSerializer::GeomRect),
+		std::visit([this](const auto &r) -> QCborArray {
+			return {
+				helper()->serializeSubtype(qMetaTypeId<std::decay_t<decltype(r.topLeft())>>(), r.topLeft(), "topLeft"),
+				helper()->serializeSubtype(qMetaTypeId<std::decay_t<decltype(r.size())>>(), r.size(), "size")
+			};
+		}, rect)
+	};
+}
+
+template<typename TSize>
+TSize GeomConverter::deserializeSize(const QCborArray &array) const
+{
+	using TW = std::decay_t<decltype(TSize{}.width())>;
+	using TH = std::decay_t<decltype(TSize{}.height())>;
+	if (array.size() != 2)
+		throw DeserializationException{"A size requires an array with exactly two numbers"};
+	return {
+		extract<TW>(array[0]),
+		extract<TH>(array[1])
+	};
+}
+
+template<typename TPoint>
+TPoint GeomConverter::deserializePoint(const QCborArray &array) const
+{
+	using TX = std::decay_t<decltype(TPoint{}.x())>;
+	using TY = std::decay_t<decltype(TPoint{}.y())>;
+	if (array.size() != 2)
+		throw DeserializationException{"A point requires an array with exactly two numbers"};
+	return {
+		extract<TX>(array[0]),
+		extract<TY>(array[1])
+	};
+}
+
+template<typename TLine>
+TLine GeomConverter::deserializeLine(const QCborArray &array) const
+{
+	using TP1 = std::decay_t<decltype(TLine{}.p1())>;
+	using TP2 = std::decay_t<decltype(TLine{}.p2())>;
+	if (array.size() != 2)
+		throw DeserializationException{"A line requires an array with exactly two points"};
+	return {
+		helper()->deserializeSubtype(qMetaTypeId<TP1>(), array[0], nullptr, "p1").template value<TP1>(),
+		helper()->deserializeSubtype(qMetaTypeId<TP2>(), array[1], nullptr, "p2").template value<TP2>()
+	};
+}
+
+template<typename TRect>
+TRect GeomConverter::deserializeRect(const QCborArray &array) const
+{
+	using TTL = std::decay_t<decltype(TRect{}.topLeft())>;
+	using TS = std::decay_t<decltype(TRect{}.size())>;
+	if (array.size() != 2)
+		throw DeserializationException{"A line requires an array with exactly two points"};
+	return {
+		helper()->deserializeSubtype(qMetaTypeId<TTL>(), array[0], nullptr, "topLeft").template value<TTL>(),
+		helper()->deserializeSubtype(qMetaTypeId<TS>(), array[1], nullptr, "size").template value<TS>()
+	};
+}
+
+template<>
+int GeomConverter::extract(const QCborValue &value) const
+{
+	if (!value.isInteger())
+		throw DeserializationException{"Expected integer, but got type " + QByteArray::number(value.type())};
+	return static_cast<int>(value.toInteger());
+}
+
+template<>
+qreal GeomConverter::extract(const QCborValue &value) const
+{
+	if (!value.isDouble() && !value.isInteger())
+		throw DeserializationException{"Expected double, but got type " + QByteArray::number(value.type())};
+	return value.toDouble();
+}

+ 52 - 0
jsonserializer/typeconverters/geomconverter_p.h

@@ -0,0 +1,52 @@
+#ifndef QTJSONSERIALIZER_GEOMCONVERTER_P_H
+#define QTJSONSERIALIZER_GEOMCONVERTER_P_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+#include <QtCore/QCborArray>
+#include <QtCore/QSize>
+#include <QtCore/QPoint>
+#include <QtCore/QLine>
+#include <QtCore/QRect>
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT GeomConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(GeomConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	int guessType(QCborTag tag, QCborValue::Type dataType) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+
+private:
+	QCborValue serializeSize(const std::variant<QSize, QSizeF> &size) const;
+	QCborValue serializePoint(const std::variant<QPoint, QPointF> &point) const;
+	QCborValue serializeLine(const std::variant<QLine, QLineF> &line) const;
+	QCborValue serializeRect(const std::variant<QRect, QRectF> &rect) const;
+
+	template <typename TSize>
+	TSize deserializeSize(const QCborArray &array) const;
+	template <typename TPoint>
+	TPoint deserializePoint(const QCborArray &array) const;
+	template <typename TLine>
+	TLine deserializeLine(const QCborArray &array) const;
+	template <typename TRect>
+	TRect deserializeRect(const QCborArray &array) const;
+	template <typename TValue>
+	TValue extract(const QCborValue &value) const;
+};
+
+template <>
+int GeomConverter::extract(const QCborValue &value) const;
+
+template <>
+qreal GeomConverter::extract(const QCborValue &value) const;
+
+}
+
+#endif // QTJSONSERIALIZER_GEOMCONVERTER_P_H

+ 150 - 0
jsonserializer/typeconverters/legacygeomconverter.cpp

@@ -0,0 +1,150 @@
+#include "legacygeomconverter_p.h"
+#include "exception.h"
+
+#include <QtCore/QCborMap>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+LegacyGeomConverter::LegacyGeomConverter()
+{
+	setPriority(Priority::VeryLow);
+}
+
+bool LegacyGeomConverter::canConvert(int metaTypeId) const
+{
+	static const QVector<int> types {
+		QMetaType::QSize,
+		QMetaType::QSizeF,
+		QMetaType::QPoint,
+		QMetaType::QPointF,
+		QMetaType::QLine,
+		QMetaType::QLineF,
+		QMetaType::QRect,
+		QMetaType::QRectF,
+		};
+	return types.contains(metaTypeId);
+}
+
+QList<QCborValue::Type> LegacyGeomConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::Map};
+}
+
+QCborValue LegacyGeomConverter::serialize(int propertyType, const QVariant &value) const
+{
+	Q_UNUSED(propertyType)
+	Q_UNUSED(value)
+	throw SerializationException{"The QJsonLegacyGeomConverter cannot serialize data"};
+}
+
+QVariant LegacyGeomConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(propertyType)
+	Q_UNUSED(value)
+	Q_UNUSED(parent)
+	throw DeserializationException{"The QJsonLegacyGeomConverter cannot deserialize CBOR data"};
+}
+
+QVariant LegacyGeomConverter::deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(parent)
+	const auto map = value.toMap();
+	switch (propertyType) {
+	case QMetaType::QSize:
+		return deserializeSize<QSize>(map);
+	case QMetaType::QSizeF:
+		return deserializeSize<QSizeF>(map);
+	case QMetaType::QPoint:
+		return deserializePoint<QPoint>(map);
+	case QMetaType::QPointF:
+		return deserializePoint<QPointF>(map);
+	case QMetaType::QLine:
+		return deserializeLine<QLine>(map);
+	case QMetaType::QLineF:
+		return deserializeLine<QLineF>(map);
+	case QMetaType::QRect:
+		return deserializeRect<QRect>(map);
+	case QMetaType::QRectF:
+		return deserializeRect<QRectF>(map);
+	default:
+		throw DeserializationException{"Invalid type id"};
+	}
+}
+
+template<typename TSize>
+TSize LegacyGeomConverter::deserializeSize(const QCborMap &map) const
+{
+	using TW = std::decay_t<decltype(TSize{}.width())>;
+	using TH = std::decay_t<decltype(TSize{}.height())>;
+	if (map.size() != 2 ||
+		!map.contains(QStringLiteral("width")) ||
+		!map.contains(QStringLiteral("height")))
+		throw DeserializationException("JSON object has no width or height properties or does have extra properties");
+	return {
+		extract<TW>(map[QStringLiteral("width")]),
+		extract<TH>(map[QStringLiteral("height")])
+	};
+}
+
+template<typename TPoint>
+TPoint LegacyGeomConverter::deserializePoint(const QCborMap &map) const
+{
+	using TX = std::decay_t<decltype(TPoint{}.x())>;
+	using TY = std::decay_t<decltype(TPoint{}.y())>;
+	if (map.size() != 2 ||
+		!map.contains(QStringLiteral("x")) ||
+		!map.contains(QStringLiteral("y")))
+		throw DeserializationException("JSON object has no x or y properties or does have extra properties");
+	return {
+		extract<TX>(map[QStringLiteral("x")]),
+		extract<TY>(map[QStringLiteral("y")])
+	};
+}
+
+template<typename TLine>
+TLine LegacyGeomConverter::deserializeLine(const QCborMap &map) const
+{
+	using TP1 = std::decay_t<decltype(TLine{}.p1())>;
+	using TP2 = std::decay_t<decltype(TLine{}.p2())>;
+	if (map.size() != 2 ||
+		!map.contains(QStringLiteral("p1")) ||
+		!map.contains(QStringLiteral("p2")))
+		throw DeserializationException("JSON object has no p1 or p2 properties or does have extra properties");
+	return {
+		helper()->deserializeSubtype(qMetaTypeId<TP1>(), map[QStringLiteral("p1")], nullptr, "p1").template value<TP1>(),
+		helper()->deserializeSubtype(qMetaTypeId<TP2>(), map[QStringLiteral("p2")], nullptr, "p2").template value<TP2>()
+	};
+}
+
+template<typename TRect>
+TRect LegacyGeomConverter::deserializeRect(const QCborMap &map) const
+{
+	using TTL = std::decay_t<decltype(TRect{}.topLeft())>;
+	using TBR = std::decay_t<decltype(TRect{}.bottomRight())>;
+	if (map.size() != 2 ||
+		!map.contains(QStringLiteral("topLeft")) ||
+		!map.contains(QStringLiteral("bottomRight")))
+		throw DeserializationException("JSON object has no topLeft or bottomRight properties or does have extra properties");
+	return {
+		helper()->deserializeSubtype(qMetaTypeId<TTL>(), map[QStringLiteral("topLeft")], nullptr, "topLeft").template value<TTL>(),
+		helper()->deserializeSubtype(qMetaTypeId<TBR>(), map[QStringLiteral("bottomRight")], nullptr, "bottomRight").template value<TBR>()
+	};
+}
+
+template<>
+int LegacyGeomConverter::extract(const QCborValue &value) const
+{
+	if (!value.isInteger())
+		throw DeserializationException{"Expected integer, but got type " + QByteArray::number(value.type())};
+	return static_cast<int>(value.toInteger());
+}
+
+template<>
+qreal LegacyGeomConverter::extract(const QCborValue &value) const
+{
+	if (!value.isDouble() && !value.isInteger())
+		throw DeserializationException{"Expected double, but got type " + QByteArray::number(value.type())};
+	return value.toDouble();
+}

+ 48 - 0
jsonserializer/typeconverters/legacygeomconverter_p.h

@@ -0,0 +1,48 @@
+#ifndef QTJSONSERIALIZER_LEGACYGEOMCONVERTER_P_H
+#define QTJSONSERIALIZER_LEGACYGEOMCONVERTER_P_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+#include <QtCore/QCborArray>
+#include <QtCore/QSize>
+#include <QtCore/QPoint>
+#include <QtCore/QLine>
+#include <QtCore/QRect>
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT LegacyGeomConverter : public TypeConverter
+{
+public:
+	LegacyGeomConverter();
+
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(LegacyGeomConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+	QVariant deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const override;
+
+private:
+	template <typename TSize>
+	TSize deserializeSize(const QCborMap &map) const;
+	template <typename TPoint>
+	TPoint deserializePoint(const QCborMap &map) const;
+	template <typename TLine>
+	TLine deserializeLine(const QCborMap &map) const;
+	template <typename TRect>
+	TRect deserializeRect(const QCborMap &map) const;
+	template <typename TValue>
+	TValue extract(const QCborValue &value) const;
+};
+
+template <>
+int LegacyGeomConverter::extract(const QCborValue &value) const;
+
+template <>
+qreal LegacyGeomConverter::extract(const QCborValue &value) const;
+
+}
+
+#endif // QTJSONSERIALIZER_LEGACYGEOMCONVERTER_P_H

+ 87 - 0
jsonserializer/typeconverters/listconverter.cpp

@@ -0,0 +1,87 @@
+#include "listconverter_p.h"
+#include "exception.h"
+#include "cborserializer.h"
+#include "metawriters.h"
+
+#include <QtCore/QJsonArray>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+using namespace QtJsonSerializer::MetaWriters;
+
+bool ListConverter::canConvert(int metaTypeId) const
+{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	return QVariant{metaTypeId, nullptr}.canConvert(QMetaType::QVariantList) &&
+		   SequentialWriter::canWrite(metaTypeId);
+#else
+	return QVariant{QMetaType(metaTypeId), nullptr}.canConvert(QMetaType(QMetaType::QVariantList)) &&
+		   SequentialWriter::canWrite(metaTypeId);
+#endif
+}
+
+QList<QCborTag> ListConverter::allowedCborTags(int metaTypeId) const
+{
+	const auto isSet = SequentialWriter::getInfo(metaTypeId).isSet;
+	QList<QCborTag> tags {
+		NoTag,
+		static_cast<QCborTag>(CborSerializer::Homogeneous)
+	};
+	if (isSet)
+		tags.append(static_cast<QCborTag>(CborSerializer::Set));
+	return tags;
+}
+
+QList<QCborValue::Type> ListConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::Array};
+}
+
+QCborValue ListConverter::serialize(int propertyType, const QVariant &value) const
+{
+	const auto info = SequentialWriter::getInfo(propertyType);
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	if (!value.canConvert(QMetaType::QVariantList)) {
+#else
+	if (!value.canConvert(QMetaType(QMetaType::QVariantList))) {
+#endif
+		throw SerializationException(QByteArray("Given type ") +
+										  QMetaTypeName(propertyType) +
+										  QByteArray(" cannot be processed via QSequentialIterable - make shure to register the container type via Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE"));
+	}
+
+	QCborArray array;
+	auto index = 0;
+	for (const auto &element : value.value<QSequentialIterable>())
+		array.append(helper()->serializeSubtype(info.type, element, "[" + QByteArray::number(index++) + "]"));
+	if (info.isSet)
+		return {static_cast<QCborTag>(CborSerializer::Set), array};
+	else
+		return array;
+}
+
+QVariant ListConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	//generate the list
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	QVariant list{propertyType, nullptr};
+#else
+	QVariant list{QMetaType(propertyType), nullptr};
+#endif
+	auto writer = SequentialWriter::getWriter(list);
+	if (!writer) {
+		throw DeserializationException(QByteArray("Given type ") +
+											QMetaTypeName(propertyType) +
+											QByteArray(" cannot be accessed via QSequentialWriter - make shure to register it via QJsonSerializerBase::registerListConverters or QJsonSerializerBase::registerSetConverters"));
+	}
+
+	const auto info = writer->info();
+	const auto array = (value.isTag() ? value.taggedValue() : value).toArray();
+	auto index = 0;
+	writer->reserve(static_cast<int>(array.size()));
+	for (auto element : array)
+		writer->add(helper()->deserializeSubtype(info.type, element, parent, "[" + QByteArray::number(index++) + "]"));
+	return list;
+}

+ 22 - 0
jsonserializer/typeconverters/listconverter_p.h

@@ -0,0 +1,22 @@
+#ifndef QTJSONSERIALIZER_LISTCONVERTER_P_H
+#define QTJSONSERIALIZER_LISTCONVERTER_P_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT ListConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(ListConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_LISTCONVERTER_P_H

+ 60 - 0
jsonserializer/typeconverters/localeconverter.cpp

@@ -0,0 +1,60 @@
+#include "localeconverter_p.h"
+#include "exception.h"
+#include "cborserializer.h"
+
+#include <QtCore/QLocale>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+
+bool LocaleConverter::canConvert(int metaTypeId) const
+{
+	return metaTypeId == QMetaType::QLocale;
+}
+
+QList<QCborTag> LocaleConverter::allowedCborTags(int metaTypeId) const
+{
+	Q_UNUSED(metaTypeId)
+	return {
+		static_cast<QCborTag>(CborSerializer::LocaleISO),
+		static_cast<QCborTag>(CborSerializer::LocaleBCP47),
+	};
+}
+
+QList<QCborValue::Type> LocaleConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::String};
+}
+
+int LocaleConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
+{
+	return allowedCborTags(QMetaType::UnknownType).contains(tag) &&
+		   dataType == QCborValue::String;
+}
+
+QCborValue LocaleConverter::serialize(int propertyType, const QVariant &value) const
+{
+	Q_UNUSED(propertyType)
+	if (helper()->getProperty("useBcp47Locale").toBool())
+		return {static_cast<QCborTag>(CborSerializer::LocaleBCP47), value.toLocale().bcp47Name()};
+	else
+		return {static_cast<QCborTag>(CborSerializer::LocaleISO), value.toLocale().name()};
+}
+
+QVariant LocaleConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	Q_UNUSED(propertyType)
+	Q_UNUSED(parent)
+
+	const auto strValue = (value.isTag() ? value.taggedValue() : value).toString();
+	QLocale locale{strValue};
+	if (locale == QLocale::c()) {
+		if (strValue.toUpper() == QLatin1Char('C') ||
+			strValue.isEmpty())
+			return QLocale::c();
+		else
+			throw DeserializationException("String cannot be interpreted as locale");
+	} else
+		return locale;
+}

+ 23 - 0
jsonserializer/typeconverters/localeconverter_p.h

@@ -0,0 +1,23 @@
+#ifndef QTJSONSERIALIZER_LOCALECONVERTER_P_H
+#define QTJSONSERIALIZER_LOCALECONVERTER_P_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT LocaleConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(LocaleConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	int guessType(QCborTag tag, QCborValue::Type dataType) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_LOCALECONVERTER_P_H

+ 93 - 0
jsonserializer/typeconverters/mapconverter.cpp

@@ -0,0 +1,93 @@
+#include "mapconverter_p.h"
+#include "exception.h"
+#include "cborserializer.h"
+#include "metawriters.h"
+
+#include <QtCore/QJsonObject>
+using namespace QtJsonSerializer;
+using namespace QtJsonSerializer::TypeConverters;
+using namespace QtJsonSerializer::MetaWriters;
+
+bool MapConverter::canConvert(int metaTypeId) const
+{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	const QVariant tValue{metaTypeId, nullptr};
+	return (tValue.canConvert(QMetaType::QVariantMap) ||
+			tValue.canConvert(QMetaType::QVariantHash)) &&
+		   AssociativeWriter::canWrite(metaTypeId);
+
+#else
+	const QVariant tValue{QMetaType(metaTypeId), nullptr};
+	return (tValue.canConvert(QMetaType(QMetaType::QVariantMap)) ||
+			tValue.canConvert(QMetaType(QMetaType::QVariantHash))) &&
+		   AssociativeWriter::canWrite(metaTypeId);
+
+#endif
+}
+
+QList<QCborTag> MapConverter::allowedCborTags(int metaTypeId) const
+{
+	Q_UNUSED(metaTypeId)
+	return {NoTag, static_cast<QCborTag>(CborSerializer::ExplicitMap)};
+}
+
+QList<QCborValue::Type> MapConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
+{
+	Q_UNUSED(metaTypeId)
+	Q_UNUSED(tag)
+	return {QCborValue::Map};
+}
+
+QCborValue MapConverter::serialize(int propertyType, const QVariant &value) const
+{
+	const auto info = AssociativeWriter::getInfo(propertyType);
+
+	// verify is readable
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	if (!value.canConvert(QMetaType::QVariantMap) &&
+		!value.canConvert(QMetaType::QVariantHash)) {
+#else
+	if (!value.canConvert(QMetaType(QMetaType::QVariantMap)) &&
+		!value.canConvert(QMetaType(QMetaType::QVariantHash))) {
+#endif
+		throw SerializationException(QByteArray("Given type ") +
+										  QMetaTypeName(propertyType) +
+										  QByteArray(" cannot be processed via QAssociativeIterable - make shure to register the container type via Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE"));
+	}
+
+	// write from map to cbor
+	const auto iterable = value.value<QAssociativeIterable>();
+	QCborMap cborMap;
+	for (auto it = iterable.begin(), end = iterable.end(); it != end; ++it) {
+		const QByteArray keyStr = "[" + it.key().toString().toUtf8() + "]";
+		cborMap.insert(helper()->serializeSubtype(info.keyType, it.key(), keyStr + ".key"),
+					   helper()->serializeSubtype(info.valueType, it.value(), keyStr + ".value"));
+	}
+	return cborMap;
+}
+
+QVariant MapConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
+{
+	//generate the map
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+	QVariant map{propertyType, nullptr};
+#else
+	QVariant map{QMetaType(propertyType), nullptr};
+#endif
+	auto writer = AssociativeWriter::getWriter(map);
+	if (!writer) {
+		throw DeserializationException(QByteArray("Given type ") +
+											QMetaTypeName(propertyType) +
+											QByteArray(" cannot be accessed via QAssociativeWriter - make shure to register it via QJsonSerializerBase::registerMapConverters"));
+	}
+
+	// write from cbor into the map
+	const auto info = writer->info();
+	const auto cborMap = (value.isTag() ? value.taggedValue() : value).toMap();
+	for (const auto entry : cborMap) {
+		const QByteArray keyStr = "[" + entry.first.toVariant().toString().toUtf8() + "]";
+		writer->add(helper()->deserializeSubtype(info.keyType, entry.first, parent, keyStr + ".key"),
+					helper()->deserializeSubtype(info.valueType, entry.second, parent, keyStr + ".value"));
+	}
+	return map;
+}

+ 22 - 0
jsonserializer/typeconverters/mapconverter_p.h

@@ -0,0 +1,22 @@
+#ifndef QTJSONSERIALIZER_MAPCONVERTER_P_H
+#define QTJSONSERIALIZER_MAPCONVERTER_P_H
+
+#include "qtjsonserializer_global.h"
+#include "typeconverter.h"
+
+namespace QtJsonSerializer::TypeConverters {
+
+class Q_JSONSERIALIZER_EXPORT MapConverter : public TypeConverter
+{
+public:
+	QT_JSONSERIALIZER_TYPECONVERTER_NAME(MapConverter)
+	bool canConvert(int metaTypeId) const override;
+	QList<QCborTag> allowedCborTags(int metaTypeId) const override;
+	QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override;
+	QCborValue serialize(int propertyType, const QVariant &value) const override;
+	QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override;
+};
+
+}
+
+#endif // QTJSONSERIALIZER_MAPCONVERTER_P_H

Неке датотеке нису приказане због велике количине промена