multimapconverter.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #include "multimapconverter_p.h"
  2. #include "exception.h"
  3. #include "cborserializer.h"
  4. #include <QtCore/QJsonObject>
  5. #include <QtCore/QJsonArray>
  6. using namespace QtJsonSerializer;
  7. using namespace QtJsonSerializer::TypeConverters;
  8. using namespace QtJsonSerializer::MetaWriters;
  9. bool MultiMapConverter::canConvert(int metaTypeId) const
  10. {
  11. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  12. const QVariant tValue{metaTypeId, nullptr};
  13. return (tValue.canConvert(QMetaType::QVariantMap) ||
  14. tValue.canConvert(QMetaType::QVariantHash)) &&
  15. AssociativeWriter::canWrite(metaTypeId);
  16. #else
  17. const QVariant tValue{QMetaType(metaTypeId), nullptr};
  18. return (tValue.canConvert(QMetaType(QMetaType::QVariantMap)) ||
  19. tValue.canConvert(QMetaType(QMetaType::QVariantHash))) &&
  20. AssociativeWriter::canWrite(metaTypeId);
  21. #endif
  22. }
  23. QList<QCborTag> MultiMapConverter::allowedCborTags(int metaTypeId) const
  24. {
  25. Q_UNUSED(metaTypeId)
  26. return {
  27. NoTag,
  28. static_cast<QCborTag>(CborSerializer::MultiMap),
  29. static_cast<QCborTag>(CborSerializer::ExplicitMap)
  30. };
  31. }
  32. QList<QCborValue::Type> MultiMapConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
  33. {
  34. Q_UNUSED(metaTypeId)
  35. Q_UNUSED(tag)
  36. return {QCborValue::Map, QCborValue::Array};
  37. }
  38. QCborValue MultiMapConverter::serialize(int propertyType, const QVariant &value) const
  39. {
  40. const auto info = AssociativeWriter::getInfo(propertyType);
  41. // verify is readable
  42. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  43. if (!value.canConvert(QMetaType::QVariantMap) &&
  44. !value.canConvert(QMetaType::QVariantHash)) {
  45. #else
  46. if (!value.canConvert(QMetaType(QMetaType::QVariantMap)) &&
  47. !value.canConvert(QMetaType(QMetaType::QVariantHash))) {
  48. #endif
  49. throw SerializationException(QByteArray("Given type ") +
  50. QMetaTypeName(propertyType) +
  51. QByteArray(" cannot be processed via QAssociativeIterable - make shure to register the container type via Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE"));
  52. }
  53. // write from map to cbor
  54. const auto mapMode = helper()->getProperty("multiMapMode").value<SerializerBase::MultiMapMode>();
  55. const auto iterable = value.value<QAssociativeIterable>();
  56. switch (mapMode) {
  57. case SerializerBase::MultiMapMode::Map:
  58. case SerializerBase::MultiMapMode::DenseMap: {
  59. QCborMap cborMap;
  60. for (auto it = iterable.begin(), end = iterable.end(); it != end; ++it) {
  61. const QByteArray keyStr = "[" + it.key().toString().toUtf8() + "]";
  62. const auto key = helper()->serializeSubtype(info.keyType, it.key(), keyStr + ".key");
  63. auto mValueRef = cborMap[key];
  64. const auto vType = mapMode == SerializerBase::MultiMapMode::DenseMap ?
  65. mValueRef.type() :
  66. QCborValue::Array;
  67. switch (vType) {
  68. case QCborValue::Array: {
  69. auto mArray = mValueRef.toArray();
  70. mValueRef = QCborValue{}; // "clear" the array spot, reducing the ref cnt on mArray to 1, so stuff is added without copying
  71. mArray.append(helper()->serializeSubtype(info.valueType, it.value(), keyStr + ".value"));
  72. mValueRef = mArray;
  73. break;
  74. }
  75. case QCborValue::Undefined:
  76. mValueRef = helper()->serializeSubtype(info.valueType, it.value(), keyStr + ".value");
  77. break;
  78. default: {
  79. QCborArray mArray {mValueRef};
  80. mArray.append(helper()->serializeSubtype(info.valueType, it.value(), keyStr + ".value"));
  81. mValueRef = mArray;
  82. break;
  83. }
  84. }
  85. }
  86. return {static_cast<QCborTag>(CborSerializer::MultiMap), cborMap};
  87. }
  88. case SerializerBase::MultiMapMode::List: {
  89. QCborArray cborArray;
  90. for (auto it = iterable.begin(), end = iterable.end(); it != end; ++it) {
  91. const QByteArray keyStr = "[" + it.key().toString().toUtf8() + "]";
  92. cborArray.append(QCborArray{
  93. helper()->serializeSubtype(info.keyType, it.key(), keyStr + ".key"),
  94. helper()->serializeSubtype(info.valueType, it.value(), keyStr + ".value")
  95. });
  96. }
  97. return {static_cast<QCborTag>(CborSerializer::MultiMap), cborArray};
  98. }
  99. default:
  100. Q_UNREACHABLE();
  101. return {};
  102. }
  103. }
  104. QVariant MultiMapConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
  105. {
  106. // generate the map
  107. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  108. QVariant map{propertyType, nullptr};
  109. #else
  110. QVariant map{QMetaType(propertyType), nullptr};
  111. #endif
  112. auto writer = AssociativeWriter::getWriter(map);
  113. if (!writer) {
  114. throw DeserializationException(QByteArray("Given type ") +
  115. QMetaTypeName(propertyType) +
  116. QByteArray(" cannot be accessed via QAssociativeWriter - make shure to register it via QJsonSerializerBase::registerMapConverters"));
  117. }
  118. // write from cbor into the map
  119. const auto info = writer->info();
  120. const auto cValue = (value.isTag() ? value.taggedValue() : value);
  121. switch (cValue.type()) {
  122. case QCborValue::Map: {
  123. for (const auto entry : cValue.toMap()) {
  124. const QByteArray keyStr = "[" + entry.first.toVariant().toString().toUtf8() + "]";
  125. const auto key = helper()->deserializeSubtype(info.keyType, entry.first, parent, keyStr + ".key");
  126. if (entry.second.isArray()) {
  127. auto cnt = 0;
  128. for (const auto aValue : entry.second.toArray())
  129. writer->add(key, helper()->deserializeSubtype(info.valueType, aValue, parent, keyStr + ".value[" + QByteArray::number(cnt++) + "]"));
  130. } else
  131. writer->add(key, helper()->deserializeSubtype(info.valueType, entry.second, parent, keyStr + ".value"));
  132. }
  133. break;
  134. }
  135. case QCborValue::Array: {
  136. for (const auto aValue : cValue.toArray()) {
  137. const auto vPair = aValue.toArray();
  138. if (vPair.size() != 2)
  139. throw DeserializationException("CBOR/JSON array must have exactly 2 elements to be read as a value of a multi map");
  140. const QByteArray keyStr = "[" + vPair[0].toVariant().toString().toUtf8() + "]";
  141. writer->add(helper()->deserializeSubtype(info.keyType, vPair[0], parent, keyStr + ".key"),
  142. helper()->deserializeSubtype(info.valueType, vPair[1], parent, keyStr + ".value"));
  143. }
  144. break;
  145. }
  146. default:
  147. throw DeserializationException("Unsupported CBOR/JSON-Type: " + QByteArray::number(value.type()));
  148. }
  149. return map;
  150. }