gadgetconverter.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #include "gadgetconverter_p.h"
  2. #include "exception.h"
  3. #include "serializerbase_p.h"
  4. #include <QtCore/QMetaProperty>
  5. #include <QtCore/QSet>
  6. using namespace QtJsonSerializer;
  7. using namespace QtJsonSerializer::TypeConverters;
  8. bool GadgetConverter::canConvert(int metaTypeId) const
  9. {
  10. // exclude a few Qt gadgets that have no properties and thus need to be handled otherwise
  11. static const QSet<int> gadgetExceptions {
  12. QMetaType::QKeySequence,
  13. QMetaType::QFont,
  14. QMetaType::QLocale,
  15. };
  16. if(gadgetExceptions.contains(metaTypeId))
  17. return false;
  18. const auto flags = QMetaType(metaTypeId).flags();
  19. return flags.testFlag(QMetaType::IsGadget) ||
  20. flags.testFlag(QMetaType::PointerToGadget);
  21. }
  22. QList<QCborValue::Type> GadgetConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
  23. {
  24. Q_UNUSED(tag)
  25. if (QMetaType(metaTypeId).flags().testFlag(QMetaType::PointerToGadget))
  26. return {QCborValue::Map, QCborValue::Null};
  27. else
  28. return {QCborValue::Map};
  29. }
  30. QCborValue GadgetConverter::serialize(int propertyType, const QVariant &value) const
  31. {
  32. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  33. const auto metaObject = QMetaType::metaObjectForType(propertyType);
  34. #else
  35. auto metaType = QMetaType(propertyType);
  36. const auto metaObject = metaType.metaObject();
  37. #endif
  38. if (!metaObject)
  39. throw SerializationException(QByteArray("Unable to get metaobject for type ") + QMetaTypeName(propertyType));
  40. const auto isPtr = QMetaType(propertyType).flags().testFlag(QMetaType::PointerToGadget);
  41. auto gValue = value;
  42. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  43. if (!gValue.convert(propertyType))
  44. #else
  45. if (!gValue.convert(metaType))
  46. #endif
  47. throw SerializationException(QByteArray("Data is not of the required gadget type ") + QMetaTypeName(propertyType));
  48. const void *gadget = nullptr;
  49. if (isPtr) {
  50. // with pointers, null gadgets are allowed
  51. gadget = *reinterpret_cast<const void* const *>(gValue.constData());
  52. if (!gadget)
  53. return QCborValue::Null;
  54. } else
  55. gadget = gValue.constData();
  56. if (!gadget)
  57. throw SerializationException(QByteArray("Unable to get address of gadget ") + QMetaTypeName(propertyType));
  58. QCborMap cborMap;
  59. //go through all properties and try to serialize them
  60. const auto ignoreStoredAttribute = helper()->getProperty("ignoreStoredAttribute").toBool();
  61. for (auto i = 0; i < metaObject->propertyCount(); i++) {
  62. auto property = metaObject->property(i);
  63. if (ignoreStoredAttribute || property.isStored())
  64. cborMap[QString::fromUtf8(property.name())] = helper()->serializeSubtype(property, property.readOnGadget(gadget));
  65. }
  66. return cborMap;
  67. }
  68. QVariant GadgetConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
  69. {
  70. Q_UNUSED(parent) // gadgets neither have nor serve as parent
  71. const auto isPtr = QMetaType(propertyType).flags().testFlag(QMetaType::PointerToGadget);
  72. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  73. const auto metaObject = QMetaType::metaObjectForType(propertyType);
  74. #else
  75. auto metaType = QMetaType(propertyType);
  76. const auto metaObject = metaType.metaObject();
  77. #endif
  78. if (!metaObject)
  79. throw DeserializationException(QByteArray("Unable to get metaobject for gadget type") + QMetaTypeName(propertyType));
  80. auto cValue = value.isTag() ? value.taggedValue() : value;
  81. QVariant gadget;
  82. void *gadgetPtr = nullptr;
  83. if (isPtr) {
  84. if (cValue.isNull())
  85. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  86. return QVariant{propertyType, nullptr}; // initialize an empty (nullptr) variant
  87. const auto gadgetType = QMetaType::type(metaObject->className());
  88. if (gadgetType == QMetaType::UnknownType)
  89. throw DeserializationException(QByteArray("Unable to get type of gadget from gadget-pointer type") + QMetaTypeName(propertyType));
  90. gadgetPtr = QMetaType::create(gadgetType);
  91. gadget = QVariant{propertyType, &gadgetPtr};
  92. #else
  93. return QVariant{metaType, nullptr}; // initialize an empty (nullptr) variant
  94. auto gadgetMetaType = QMetaType::fromName(metaObject->className());
  95. if (!gadgetMetaType.isValid())
  96. throw DeserializationException(QByteArray("Unable to get type of gadget from gadget-pointer type") + QMetaTypeName(propertyType));
  97. gadgetPtr = gadgetMetaType.create();
  98. gadget = QVariant{metaType, &gadgetPtr};
  99. #endif
  100. } else {
  101. if (cValue.isNull())
  102. 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
  103. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  104. gadget = QVariant{propertyType, nullptr};
  105. #else
  106. gadget = QVariant{metaType, nullptr};
  107. #endif
  108. gadgetPtr = gadget.data();
  109. }
  110. if (!gadgetPtr) {
  111. throw DeserializationException(QByteArray("Failed to construct gadget of type ") +
  112. QMetaTypeName(propertyType) +
  113. QByteArray(". Does it have a default constructor?"));
  114. }
  115. const auto validationFlags = helper()->getProperty("validationFlags").value<SerializerBase::ValidationFlags>();
  116. const auto ignoreStoredAttribute = helper()->getProperty("ignoreStoredAttribute").toBool();
  117. // collect required properties, if set
  118. QSet<QByteArray> reqProps;
  119. if (validationFlags.testFlag(SerializerBase::ValidationFlag::AllProperties)) {
  120. for (auto i = 0; i < metaObject->propertyCount(); i++) {
  121. auto property = metaObject->property(i);
  122. if (ignoreStoredAttribute || property.isStored())
  123. reqProps.insert(property.name());
  124. }
  125. }
  126. // now deserialize all json properties
  127. const auto cborMap = cValue.toMap();
  128. for (auto it = cborMap.constBegin(); it != cborMap.constEnd(); it++) {
  129. const auto key = it.key().toString().toUtf8();
  130. const auto propIndex = metaObject->indexOfProperty(key);
  131. if (propIndex != -1) {
  132. const auto property = metaObject->property(propIndex);
  133. property.writeOnGadget(gadgetPtr, helper()->deserializeSubtype(property, it.value(), nullptr));
  134. reqProps.remove(property.name());
  135. } else if (validationFlags.testFlag(SerializerBase::ValidationFlag::NoExtraProperties)) {
  136. throw DeserializationException("Found extra property " +
  137. key +
  138. " but extra properties are not allowed");
  139. }
  140. }
  141. // make sure all required properties have been read
  142. if (validationFlags.testFlag(SerializerBase::ValidationFlag::AllProperties) && !reqProps.isEmpty()) {
  143. throw DeserializationException(QByteArray("Not all properties for ") +
  144. metaObject->className() +
  145. QByteArray(" are present in the json object. Missing properties: ") +
  146. reqProps.values().join(", "));
  147. }
  148. return gadget;
  149. }