#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 #include #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #endif #include #include #include #include #include #include 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 static void registerExtractor(); //! @copybrief SerializerBase::registerExtractor static void registerExtractor(int metaTypeId, const QSharedPointer &extractor); //! Registers a custom type for list converisons template static inline void registerListConverters(); //! Registers a custom type for set converisons template static inline void registerSetConverters(); //! Registers a custom type for map converisons template static inline void registerMapConverters(); //! Registers a custom type for QSharedPointer and QPointer converisons template static inline void registerPointerConverters(); //! Registers a custom type for list, set map and optional converisons. Also include pointer converters, if applicable template static inline void registerBasicConverters(); //! Registers two types for pair conversion template static inline void registerPairConverters(); //! Registers a number of types for std::tuple conversion template static inline void registerTupleConverters(); //! Registers a custom type for std::optional converisons template static inline void registerOptionalConverters(); //! Registers a custom type for std::variant converisons template static inline void registerVariantConverters(); //! Serializes a given variant to either CBOR or JSON, depending on the actual instance virtual std::variant serializeGeneric(const QVariant &value) const = 0; //! Deserializes CBOR or JSON, depending on the actual instance, to variant virtual QVariant deserializeGeneric(const std::variant &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 static void addJsonTypeConverterFactory(); //! @copybrief SerializerBase::addJsonTypeConverterFactory() static void addJsonTypeConverterFactory(TypeConverterFactory *factory); //! Adds a custom type converter to this serializer template void addJsonTypeConverter(); //! @copybrief SerializerBase::addJsonTypeConverter() void addJsonTypeConverter(const QSharedPointer &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 typesForTag(QCborTag tag) const = 0; // protected implementation -> internal use for the type converters QVariant getProperty(const char *name) const override; QSharedPointer 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::value, "x must be bool"); \ Q_CLASSINFO("polymorphic", #x) // ------------- Generic Implementation ------------- template void SerializerBase::registerExtractor() { registerExtractor(qMetaTypeId(), QSharedPointer::create()); } template void SerializerBase::registerListConverters() { MetaWriters::SequentialWriter::registerWriter(); #if !defined(QT_NO_LINKED_LIST) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) MetaWriters::SequentialWriter::registerWriter(); #endif MetaWriters::SequentialWriter::registerWriter(); MetaWriters::SequentialWriter::registerWriter(); MetaWriters::SequentialWriter::registerWriter(); } template void SerializerBase::registerSetConverters() { MetaWriters::SequentialWriter::registerWriter(); } template void SerializerBase::registerMapConverters() { if constexpr (mapTypes) { MetaWriters::AssociativeWriter::registerWriter(); MetaWriters::AssociativeWriter::registerWriter(); } if constexpr (hashTypes) { MetaWriters::AssociativeWriter::registerWriter(); MetaWriters::AssociativeWriter::registerWriter(); } } template void SerializerBase::registerPointerConverters() { registerExtractor, TypeExtractors::SmartPointerExtractor>(); if constexpr (std::is_base_of_v) registerExtractor, TypeExtractors::SmartPointerExtractor>(); } template void SerializerBase::registerBasicConverters() { if constexpr (std::is_base_of_v) { registerBasicConverters(); registerPointerConverters(); registerBasicConverters>(); registerBasicConverters>(); } else { registerListConverters(); registerSetConverters(); registerMapConverters(); } } template void SerializerBase::registerPairConverters() { registerExtractor, TypeExtractors::PairExtractor>(); registerExtractor, TypeExtractors::PairExtractor>(); } template void SerializerBase::registerTupleConverters() { registerExtractor, TypeExtractors::TupleExtractor>(); } template void SerializerBase::registerOptionalConverters() { registerExtractor, TypeExtractors::OptionalExtractor>(); } template void SerializerBase::registerVariantConverters() { registerExtractor, TypeExtractors::VariantExtractor>(); } template void SerializerBase::addJsonTypeConverterFactory() { static_assert(std::is_base_of::value, "T must implement QJsonTypeConverter"); addJsonTypeConverterFactory(new TypeConverterStandardFactory{}); } template void SerializerBase::addJsonTypeConverter() { static_assert(std::is_base_of::value, "T must implement QJsonTypeConverter"); addJsonTypeConverter(QSharedPointer::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