objectconverter.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #include "objectconverter_p.h"
  2. #include "exception.h"
  3. #include "cborserializer.h"
  4. #include <array>
  5. using namespace QtJsonSerializer;
  6. using namespace QtJsonSerializer::TypeConverters;
  7. Q_LOGGING_CATEGORY(QtJsonSerializer::TypeConverters::logObjConverter, "qt.jsonserializer.converter.object")
  8. bool ObjectConverter::canConvert(int metaTypeId) const
  9. {
  10. auto flags = QMetaType(metaTypeId).flags();
  11. return flags.testFlag(QMetaType::PointerToQObject);
  12. }
  13. QList<QCborTag> ObjectConverter::allowedCborTags(int metaTypeId) const
  14. {
  15. Q_UNUSED(metaTypeId)
  16. return {
  17. NoTag,
  18. static_cast<QCborTag>(CborSerializer::GenericObject),
  19. static_cast<QCborTag>(CborSerializer::ConstructedObject)
  20. };
  21. }
  22. QList<QCborValue::Type> ObjectConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
  23. {
  24. Q_UNUSED(metaTypeId)
  25. switch (static_cast<quint64>(tag)) {
  26. case CborSerializer::GenericObject:
  27. return {QCborValue::Array};
  28. case CborSerializer::ConstructedObject:
  29. return {QCborValue::Array, QCborValue::Null};
  30. default:
  31. return {QCborValue::Map, QCborValue::Null};
  32. }
  33. }
  34. int ObjectConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
  35. {
  36. Q_UNUSED(dataType)
  37. switch (static_cast<quint64>(tag)) {
  38. case CborSerializer::GenericObject:
  39. case CborSerializer::ConstructedObject:
  40. return QMetaType::QObjectStar;
  41. default:
  42. return QMetaType::UnknownType;
  43. }
  44. }
  45. QCborValue ObjectConverter::serialize(int propertyType, const QVariant &value) const
  46. {
  47. auto object = value.value<QObject*>();
  48. if (!object)
  49. return QCborValue::Null;
  50. QCborMap cborMap;
  51. // get the metaobject, based on polymorphism
  52. const QMetaObject *metaObject = nullptr;
  53. auto poly = static_cast<SerializerBase::Polymorphing>(helper()->getProperty("polymorphing").toInt());
  54. auto isPoly = false;
  55. switch (poly) {
  56. case SerializerBase::Polymorphing::Disabled:
  57. isPoly = false;
  58. break;
  59. case SerializerBase::Polymorphing::Enabled:
  60. isPoly = polyMetaObject(object);
  61. break;
  62. case SerializerBase::Polymorphing::Forced:
  63. isPoly = true;
  64. break;
  65. default:
  66. Q_UNREACHABLE();
  67. break;
  68. }
  69. if (isPoly) {
  70. metaObject = object->metaObject();
  71. //first: pass the class name
  72. cborMap[QStringLiteral("@class")] = QString::fromUtf8(metaObject->className());
  73. } else
  74. metaObject = QMetaType(propertyType).metaObject();
  75. if (!metaObject)
  76. throw SerializationException(QByteArray("Unable to get metaobject for type ") + QMetaTypeName(propertyType));
  77. //go through all properties and try to serialize them
  78. const auto keepObjectName = helper()->getProperty("keepObjectName").toBool();
  79. const auto ignoreStoredAttribute = helper()->getProperty("ignoreStoredAttribute").toBool();
  80. auto i = QObject::staticMetaObject.indexOfProperty("objectName");
  81. if (!keepObjectName)
  82. i++;
  83. for(; i < metaObject->propertyCount(); i++) {
  84. auto property = metaObject->property(i);
  85. if (ignoreStoredAttribute || property.isStored())
  86. cborMap[QString::fromUtf8(property.name())] = helper()->serializeSubtype(property, property.read(object));
  87. }
  88. return cborMap;
  89. }
  90. QVariant ObjectConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
  91. {
  92. if ((value.isTag() ? value.taggedValue() : value).isNull())
  93. return QVariant::fromValue<QObject*>(nullptr);
  94. QCborMap cborMap;
  95. if (value.isTag()) {
  96. if (value.tag() == static_cast<QCborTag>(CborSerializer::GenericObject))
  97. return QVariant::fromValue(deserializeGenericObject(value.taggedValue().toArray(), parent));
  98. else if (value.tag() == static_cast<QCborTag>(CborSerializer::ConstructedObject))
  99. return QVariant::fromValue(deserializeConstructedObject(value.taggedValue(), parent));
  100. else
  101. cborMap = value.taggedValue().toMap();
  102. } else
  103. cborMap = value.toMap();
  104. auto poly = static_cast<SerializerBase::Polymorphing>(helper()->getProperty("polymorphing").toInt());
  105. auto metaObject = QMetaType(propertyType).metaObject();
  106. if (!metaObject)
  107. throw DeserializationException(QByteArray("Unable to get metaobject for type ") + QMetaTypeName(propertyType));
  108. // try to get the polymorphic metatype (if allowed)
  109. auto isPoly = false;
  110. if (poly != SerializerBase::Polymorphing::Disabled) {
  111. if (cborMap.contains(QStringLiteral("@class"))) {
  112. isPoly = true;
  113. QByteArray classField = cborMap[QStringLiteral("@class")].toString().toUtf8() + "*"; // add the star
  114. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  115. auto typeId = QMetaType::type(classField.constData());
  116. auto nMeta = QMetaType(typeId).metaObject();
  117. #else
  118. auto metaType = QMetaType::fromName(classField.constData());
  119. auto nMeta = metaType.metaObject();
  120. #endif
  121. if (!nMeta)
  122. throw DeserializationException("Unable to find class requested from json \"@class\" property: " + classField);
  123. if (!nMeta->inherits(metaObject)) {
  124. throw DeserializationException("Requested class from \"@class\" field, " +
  125. classField +
  126. QByteArray(", does not inhert the property type ") +
  127. QMetaTypeName(propertyType));
  128. }
  129. metaObject = nMeta;
  130. } else if (poly == SerializerBase::Polymorphing::Forced)
  131. throw DeserializationException("Json does not contain the \"@class\" field, but forced polymorphism requires it");
  132. }
  133. // try to construct the object
  134. auto object = metaObject->newInstance(Q_ARG(QObject*, parent));
  135. if (!object) {
  136. throw DeserializationException(QByteArray("Failed to construct object of type ") +
  137. metaObject->className() +
  138. QByteArray(" (Does the constructor \"Q_INVOKABLE class(QObject*);\" exist?)"));
  139. }
  140. deserializeProperties(metaObject, object, cborMap, isPoly);
  141. return QVariant::fromValue(object);
  142. }
  143. bool ObjectConverter::polyMetaObject(QObject *object) const
  144. {
  145. auto meta = object->metaObject();
  146. //check the internal property
  147. if (object->dynamicPropertyNames().contains("__qt_json_serializer_polymorphic")) {
  148. auto polyProp = object->property("__qt_json_serializer_polymorphic").toBool();
  149. return polyProp;
  150. }
  151. //check the class info
  152. auto polyIndex = meta->indexOfClassInfo("polymorphic");
  153. if (polyIndex != -1) {
  154. auto info = meta->classInfo(polyIndex);
  155. if (info.value() == QByteArray("true"))
  156. return true;// use the object
  157. else if (info.value() == QByteArray("false"))
  158. return false;// use the class
  159. else
  160. qCWarning(logObjConverter) << "Invalid value for polymorphic classinfo on object type" << meta->className() << "ignored";
  161. }
  162. //default: the class
  163. return false;// use the class
  164. }
  165. QObject *ObjectConverter::deserializeGenericObject(const QCborArray &value, QObject *parent) const
  166. {
  167. if (value.size() == 0)
  168. throw DeserializationException{"A GenericObject requires at least one argument, specifying the object name"};
  169. // find a meta object
  170. QByteArray className = value.first().toString().toUtf8() + "*"; // add the star
  171. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  172. auto typeId = QMetaType::type(className.constData());
  173. auto metaObject = QMetaType(typeId).metaObject();
  174. #else
  175. auto metaType = QMetaType::fromName(className.constData());
  176. auto metaObject = metaType.metaObject();
  177. #endif
  178. if (!metaObject)
  179. throw DeserializationException("Unable to find class requested from GenericObject tagged array: " + className);
  180. // deserialize all arguments
  181. QVariantList arguments;
  182. arguments.reserve(static_cast<int>(value.size() - 1));
  183. for (auto aIdx = 1ll; aIdx < value.size(); ++aIdx)
  184. arguments.append(helper()->deserializeSubtype(QMetaType::UnknownType, value[aIdx], nullptr, className + "[" + QByteArray::number(aIdx - 1) + "]"));
  185. // find a matching constructor
  186. for (auto cIdx = 0; cIdx < metaObject->constructorCount(); ++cIdx) {
  187. const auto constructor = metaObject->constructor(cIdx);
  188. // verify same argument count (but allow extra QObject argument for parenting)
  189. auto extraObj = false;
  190. if (constructor.parameterCount() != arguments.size()) {
  191. if (constructor.parameterCount() == arguments.size() + 1 &&
  192. constructor.parameterType(constructor.parameterCount() - 1) == QMetaType::QObjectStar)
  193. extraObj = true;
  194. else
  195. continue;
  196. }
  197. // verify each argument can be converted (by converting it)
  198. auto argCopy = arguments;
  199. auto allOk = true;
  200. for (auto pIdx = 0; pIdx < argCopy.size(); ++pIdx) {
  201. const auto pType = constructor.parameterType(pIdx);
  202. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  203. if (!argCopy[pIdx].canConvert(pType) || !argCopy[pIdx].convert(pType)) {
  204. #else
  205. if (!argCopy[pIdx].canConvert(QMetaType(pType)) || !argCopy[pIdx].convert(QMetaType(pType))) {
  206. #endif
  207. allOk = false;
  208. break;
  209. }
  210. }
  211. // if all arguments could be converted -> construct the object
  212. if (allOk) {
  213. if (extraObj)
  214. argCopy.append(QVariant::fromValue(parent));
  215. Q_ASSERT(argCopy.size() <= 10);
  216. std::array<QGenericArgument, 10> gArgs;
  217. gArgs.fill(QGenericArgument{});
  218. for (auto pIdx = 0; pIdx < argCopy.size(); ++pIdx)
  219. gArgs[static_cast<size_t>(pIdx)] = {argCopy[pIdx].typeName(), argCopy[pIdx].constData()};
  220. auto object = metaObject->newInstance(gArgs[0], gArgs[1], gArgs[2], gArgs[3], gArgs[4],
  221. gArgs[5], gArgs[6], gArgs[7], gArgs[8], gArgs[9]);
  222. if (!object) {
  223. throw DeserializationException(QByteArray("Failed to construct object of type ") +
  224. metaObject->className() +
  225. QByteArray(" - deserialized constructor arguments are potentially invalid!"));
  226. }
  227. return object;
  228. }
  229. }
  230. throw DeserializationException{"Unable to find any constructor for " +
  231. className +
  232. " with " +
  233. QByteArray::number(arguments.size()) +
  234. " arguments and matching types!"};
  235. }
  236. QObject *ObjectConverter::deserializeConstructedObject(const QCborValue &value, QObject *parent) const
  237. {
  238. if (value.isNull())
  239. return nullptr;
  240. const auto cborArray = value.toArray();
  241. if (cborArray.size() != 2)
  242. throw DeserializationException{"The ConstructedObject tagged array must have exactly two elements!"};
  243. auto object = deserializeGenericObject(cborArray[0].toArray(), parent);
  244. if (!object)
  245. return nullptr;
  246. if (!cborArray[1].isNull())
  247. deserializeProperties(object->metaObject(), object, cborArray[1].toMap());
  248. return object;
  249. }
  250. void ObjectConverter::deserializeProperties(const QMetaObject *metaObject, QObject *object, const QCborMap &value, bool isPoly) const
  251. {
  252. auto validationFlags = helper()->getProperty("validationFlags").value<SerializerBase::ValidationFlags>();
  253. auto keepObjectName = helper()->getProperty("keepObjectName").toBool();
  254. // collect required properties, if set
  255. QSet<QByteArray> reqProps;
  256. if (validationFlags.testFlag(SerializerBase::ValidationFlag::AllProperties)) {
  257. const auto ignoreStoredAttribute = helper()->getProperty("ignoreStoredAttribute").toBool();
  258. auto i = QObject::staticMetaObject.indexOfProperty("objectName");
  259. if (!keepObjectName)
  260. i++;
  261. for (; i < metaObject->propertyCount(); i++) {
  262. auto property = metaObject->property(i);
  263. if(ignoreStoredAttribute || property.isStored())
  264. reqProps.insert(property.name());
  265. }
  266. }
  267. //now deserialize all json properties
  268. for (auto it = value.constBegin(); it != value.constEnd(); it++) {
  269. if (isPoly && it.key() == QStringLiteral("@class"))
  270. continue;
  271. const auto key = it.key().toString().toUtf8();
  272. const auto propIndex = metaObject->indexOfProperty(key);
  273. if (propIndex != -1) {
  274. const auto property = metaObject->property(propIndex);
  275. property.write(object, helper()->deserializeSubtype(property, it.value(), object));
  276. reqProps.remove(property.name());
  277. } else if (validationFlags.testFlag(SerializerBase::ValidationFlag::NoExtraProperties)) {
  278. throw DeserializationException("Found extra property " +
  279. key +
  280. " but extra properties are not allowed");
  281. } else
  282. object->setProperty(key, helper()->deserializeSubtype(QMetaType::UnknownType, it.value(), object, key));
  283. }
  284. //make shure all required properties have been read
  285. if (validationFlags.testFlag(SerializerBase::ValidationFlag::AllProperties) && !reqProps.isEmpty()) {
  286. throw DeserializationException(QByteArray("Not all properties for ") +
  287. metaObject->className() +
  288. QByteArray(" are present in the json object Missing properties: ") +
  289. reqProps.values().join(", "));
  290. }
  291. }