Browse Source

Implement basic json serialization

- Add json serialization for basic types
- Implement tests
TODO: Complex types list serialization is broken
Alexey Edelev 4 years ago
parent
commit
b6e01b99f3

+ 152 - 37
src/protobuf/qprotobufjsonserializer.cpp

@@ -31,29 +31,159 @@
 using namespace QtProtobuf;
 
 namespace QtProtobuf {
+
 //! \private
 class QProtobufJsonSerializerPrivate final
 {
     Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
 public:
-    QProtobufJsonSerializerPrivate(QProtobufJsonSerializer *q) : q_ptr(q) {}
+    using Serializer = std::function<QByteArray(const QVariant&)>;
+    using Deserializer = std::function<void(void)>;
+
+    struct SerializationHandlers {
+        Serializer serializer; /*!< serializer assigned to class */
+        Deserializer deserializer;/*!< deserializer assigned to class */
+    };
+
+    using SerializerRegistry = std::unordered_map<int/*metatypeid*/, SerializationHandlers>;
+
+    static QByteArray serializeFloat(const QVariant &propertyValue) {
+        bool ok = false;
+        float value = propertyValue.toFloat(&ok);
+        if (!ok) {
+            return QByteArray("NaN");
+        }
+        return QString::number(value, 'g').toUtf8();
+    }
+
+    static QByteArray serializeString(const QVariant &propertyValue) {
+        return QString("\"%1\"").arg(propertyValue.toString()).toUtf8() ;
+    }
+
+    static QByteArray serializeBytes(const QVariant &propertyValue) {
+        return QByteArray("\"") + propertyValue.toByteArray().toBase64() + "\"";
+    }
+
+    template<typename L>
+    static QByteArray serializeList(const QVariant &propertyValue) {
+        L listValue = propertyValue.value<L>();
+        QByteArray result("[");
+        for (auto value : listValue) {
+            result += QString::number(value) + ",";
+        }
+        if (listValue.size() > 0) {
+            result.resize(result.size() - 1);//Remove trailing `,`
+        }
+        result += "]";
+        return result;
+    }
+
+    static QByteArray serializeDoubleList(const QVariant &propertyValue) {
+        DoubleList listValue = propertyValue.value<DoubleList>();
+        QByteArray result("[");
+        for (auto value : listValue) {
+            result += QString::number(value, 'g').toUtf8() + ",";
+        }
+        if (listValue.size() > 0) {
+            result.resize(result.size() - 1);//Remove trailing `,`
+        }
+        result += "]";
+        return result;
+    }
+
+    static QByteArray serializeStringList(const QVariant &propertyValue) {
+        QStringList listValue = propertyValue.value<QStringList>();
+        QByteArray result("[");
+        for (auto value : listValue) {
+            result += QByteArray("\"") + value.toUtf8() + "\",";
+        }
+        if (listValue.size() > 0) {
+            result.resize(result.size() - 1);//Remove trailing `,`
+        }
+        result += "]";
+        return result;
+    }
+
+    static QByteArray serializeBytesList(const QVariant &propertyValue) {
+        QByteArrayList listValue = propertyValue.value<QByteArrayList>();
+        QByteArray result("[");
+        for (auto value : listValue) {
+            result += QByteArray("\"") + value.toBase64() + "\",";
+        }
+        if (listValue.size() > 0) {
+            result.resize(result.size() - 1);//Remove trailing `,`
+        }
+        result += "]";
+        return result;
+    }
+
+    QProtobufJsonSerializerPrivate(QProtobufJsonSerializer *q) : q_ptr(q) {
+        if (handlers.empty()) {
+            handlers[QMetaType::Float] = {QProtobufJsonSerializerPrivate::serializeFloat, []{}};
+            handlers[QMetaType::QString] = {QProtobufJsonSerializerPrivate::serializeString, []{}};
+            handlers[QMetaType::QByteArray] = {QProtobufJsonSerializerPrivate::serializeBytes, []{}};
+            handlers[qMetaTypeId<QtProtobuf::int32List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::int32List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::int64List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::int64List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::sint32List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::sint32List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::sint64List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::sint64List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::uint32List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::uint32List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::uint64List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::uint64List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::fixed32List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::fixed32List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::fixed64List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::fixed64List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::sfixed32List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::sfixed32List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::sfixed64List>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::sfixed64List>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::FloatList>()] = {QProtobufJsonSerializerPrivate::serializeList<QtProtobuf::FloatList>, []{}};
+            handlers[qMetaTypeId<QtProtobuf::DoubleList>()] = {QProtobufJsonSerializerPrivate::serializeDoubleList, []{}};
+            handlers[qMetaTypeId<QStringList>()] = {QProtobufJsonSerializerPrivate::serializeStringList,[]{}};
+            handlers[qMetaTypeId<QByteArrayList>()] = {QProtobufJsonSerializerPrivate::serializeBytesList,[]{}};
+        }
+    }
     ~QProtobufJsonSerializerPrivate() = default;
 
     QByteArray serializeProperty(const QVariant &propertyValue, const QProtobufMetaProperty &metaProperty) {
         QByteArray buffer;
+        buffer.append("\"");
+        buffer.append(metaProperty.protoPropertyName().toUtf8());
+        buffer.append("\":");
+        auto userType = metaProperty.userType();
         auto &value = QtProtobufPrivate::findHandler(metaProperty.userType());
         if (value.serializer) {
             value.serializer(q_ptr, propertyValue, metaProperty, buffer);
-            return buffer;
         } else {
-            QString result("\"%1\":%2");
-            return result.arg(metaProperty.name(), propertyValue.toString()).toUtf8();
+            auto handler = handlers.find(userType);
+            if (handler != handlers.end()) {
+                buffer += handler->second.serializer(propertyValue);
+            } else {
+                buffer += propertyValue.toString().toUtf8();
+            }
         }
+        return buffer;
     }
+
+    QByteArray serializeObject(const QObject *object, const QProtobufMetaObject &metaObject) {
+        QByteArray result = "{";
+        for (const auto &field : metaObject.propertyOrdering) {
+            int propertyIndex = field.second;
+            int fieldIndex = field.first;
+            Q_ASSERT_X(fieldIndex < 536870912 && fieldIndex > 0, "", "fieldIndex is out of range");
+            QMetaProperty metaProperty = metaObject.staticMetaObject.property(propertyIndex);
+            const char *propertyName = metaProperty.name();
+            const QVariant &propertyValue = object->property(propertyName);
+            result.append(serializeProperty(propertyValue, QProtobufMetaProperty(metaProperty, fieldIndex)));
+            result.append(",");
+        }
+        result.resize(result.size() - 1);//Remove trailing `,`
+        result.append("}");
+        return result;
+    }
+
 private:
+    static SerializerRegistry handlers;
     QProtobufJsonSerializer *q_ptr;
 };
 
+QProtobufJsonSerializerPrivate::SerializerRegistry QProtobufJsonSerializerPrivate::handlers = {};
+
 }
 
 QProtobufJsonSerializer::QProtobufJsonSerializer() : dPtr(new QProtobufJsonSerializerPrivate(this))
@@ -65,20 +195,7 @@ QProtobufJsonSerializer::~QProtobufJsonSerializer() = default;
 
 QByteArray QProtobufJsonSerializer::serializeMessage(const QObject *object, const QProtobufMetaObject &metaObject) const
 {
-    QByteArray result = "{";
-
-    for (const auto &field : metaObject.propertyOrdering) {
-        int propertyIndex = field.second;
-        int fieldIndex = field.first;
-        Q_ASSERT_X(fieldIndex < 536870912 && fieldIndex > 0, "", "fieldIndex is out of range");
-        QMetaProperty metaProperty = metaObject.staticMetaObject.property(propertyIndex);
-        const char *propertyName = metaProperty.name();
-        const QVariant &propertyValue = object->property(propertyName);
-        result.append(dPtr->serializeProperty(propertyValue, QProtobufMetaProperty(metaProperty, fieldIndex)));
-    }
-
-    result.append("}");
-    return result;
+    return dPtr->serializeObject(object, metaObject);
 }
 
 void QProtobufJsonSerializer::deserializeMessage(QObject *object, const QProtobufMetaObject &metaObject, const QByteArray &data) const
@@ -90,10 +207,7 @@ void QProtobufJsonSerializer::deserializeMessage(QObject *object, const QProtobu
 
 QByteArray QProtobufJsonSerializer::serializeObject(const QObject *object, const QProtobufMetaObject &metaObject, const QProtobufMetaProperty &/*metaProperty*/) const
 {
-    QByteArray result = "{";
-    result.append(serializeMessage(object, metaObject));
-    result.append("}");
-    return result;
+    return dPtr->serializeObject(object, metaObject);
 }
 
 void QProtobufJsonSerializer::deserializeObject(QObject *object, const QProtobufMetaObject &metaObject, QProtobufSelfcheckIterator &it) const
@@ -103,12 +217,9 @@ void QProtobufJsonSerializer::deserializeObject(QObject *object, const QProtobuf
     Q_UNUSED(metaObject)
 }
 
-QByteArray QProtobufJsonSerializer::serializeListObject(const QObject *object, const QProtobufMetaObject &metaObject, const QProtobufMetaProperty &metaProperty) const
+QByteArray QProtobufJsonSerializer::serializeListObject(const QObject *object, const QProtobufMetaObject &metaObject, const QProtobufMetaProperty &/*metaProperty*/) const
 {
-    Q_UNUSED(object)
-    Q_UNUSED(metaObject)
-    Q_UNUSED(metaProperty)
-    return QByteArray();
+    return dPtr->serializeObject(object, metaObject);
 }
 
 void QProtobufJsonSerializer::deserializeListObject(QObject *object, const QProtobufMetaObject &metaObject, QProtobufSelfcheckIterator &it) const
@@ -133,20 +244,24 @@ void QProtobufJsonSerializer::deserializeMapPair(QVariant &key, QVariant &value,
     Q_UNUSED(it)
 }
 
-QByteArray QProtobufJsonSerializer::serializeEnum(int64 value, const QMetaEnum &metaEnum, const QtProtobuf::QProtobufMetaProperty &metaProperty) const
+QByteArray QProtobufJsonSerializer::serializeEnum(int64 value, const QMetaEnum &metaEnum, const QtProtobuf::QProtobufMetaProperty &/*metaProperty*/) const
 {
-    Q_UNUSED(value)
-    Q_UNUSED(metaEnum)
-    Q_UNUSED(metaProperty)
-    return QByteArray();
+    return QByteArray("\"") + metaEnum.key(static_cast<int>(value)) + "\"";
 }
 
-QByteArray QProtobufJsonSerializer::serializeEnumList(const QList<int64> &value, const QMetaEnum &metaEnum, const QtProtobuf::QProtobufMetaProperty &metaProperty) const
+QByteArray QProtobufJsonSerializer::serializeEnumList(const QList<int64> &values, const QMetaEnum &metaEnum, const QtProtobuf::QProtobufMetaProperty &/*metaProperty*/) const
 {
-    Q_UNUSED(value)
-    Q_UNUSED(metaEnum)
-    Q_UNUSED(metaProperty)
-    return QByteArray();
+    QByteArray result = "[";
+    for (auto value : values) {
+        result.append("\"");
+        result.append(metaEnum.key(static_cast<int>(value)));
+        result.append("\",");
+    }
+    if (values.size() > 0) {
+        result.resize(result.size() - 1);
+    }
+    result.append("]");
+    return result;
 }
 
 void QProtobufJsonSerializer::deserializeEnum(int64 &value, const QMetaEnum &metaEnum, QProtobufSelfcheckIterator &it) const

+ 36 - 0
src/protobuf/qprotobufmetaproperty.cpp

@@ -24,6 +24,22 @@
  */
 
 #include "qprotobufmetaproperty.h"
+#include "qtprotobuftypes.h"
+
+#include <string>
+
+//TODO: Code under unnamed namespace should be moved to the common header that is relevant for generator and metaproperty
+namespace  {
+int constexpr constexpr_strlen(const char* str)
+{
+    return *str ? 1 + constexpr_strlen(str + 1) : 0;
+}
+const std::vector<std::string> ListOfQmlExeptions{"id", "property", "import"};
+constexpr const char *privateSuffix = "_p";
+constexpr const char *protoSuffix = "_proto";
+constexpr int privateSuffixLenght = constexpr_strlen(privateSuffix);
+constexpr int protoSuffixLenght = constexpr_strlen(protoSuffix);
+}
 
 using namespace QtProtobuf;
 QProtobufMetaProperty::QProtobufMetaProperty(const QMetaProperty &metaProperty, int fieldIndex) : QMetaProperty(metaProperty)
@@ -31,3 +47,23 @@ QProtobufMetaProperty::QProtobufMetaProperty(const QMetaProperty &metaProperty,
 {
 
 }
+
+QString QProtobufMetaProperty::protoPropertyName() const
+{
+    QString protoName(name());
+    if ((userType() == qMetaTypeId<QtProtobuf::int32>()
+            || userType() == qMetaTypeId<QtProtobuf::fixed32>()
+            || userType() == qMetaTypeId<QtProtobuf::sfixed32>())
+            && protoName.endsWith(privateSuffix)) {
+        return protoName.mid(0, protoName.size() - privateSuffixLenght);
+    }
+
+    if (protoName.endsWith(protoSuffix)) {
+        auto tmpProtoName = protoName.mid(0, protoName.size() - protoSuffixLenght);
+        if (std::find(ListOfQmlExeptions.begin(), ListOfQmlExeptions.end(), protoName.toStdString()) != ListOfQmlExeptions.end()) {
+            return tmpProtoName;
+        }
+    }
+
+    return protoName;
+}

+ 1 - 0
src/protobuf/qprotobufmetaproperty.h

@@ -40,6 +40,7 @@ class Q_PROTOBUF_EXPORT QProtobufMetaProperty : public QMetaProperty
 public:
     QProtobufMetaProperty(const QMetaProperty &, int fieldIndex);
     int protoFieldIndex() const { return m_fieldIndex; }
+    QString protoPropertyName() const;
 private:
     QProtobufMetaProperty();
     int m_fieldIndex;

File diff suppressed because it is too large
+ 138 - 1
tests/test_protobuf/jsonserializationtest.cpp


+ 1 - 1
tests/test_wellknowntypes/simpletest.cpp

@@ -206,7 +206,7 @@ TEST_F(WellknowntypesTest, EnumTest)
     assertMessagePropertyRegistered<Enum, QString>(1, "QString", "name");
     assertMessagePropertyRegistered<Enum, EnumValueRepeated>(2, "EnumValueRepeated", "enumvalue");
     assertMessagePropertyRegistered<Enum, OptionRepeated>(3, "OptionRepeated", "options");
-//    assertMessagePropertyRegistered<Enum, SourceContext *>(4, "SourceContext*", "sourceContext");
+    assertMessagePropertyRegistered<Enum, SourceContext *>(4, "SourceContext*", "sourceContext", true);
     assertMessagePropertyRegistered<Enum, SyntaxGadget::Syntax>(5, "google::protobuf::SyntaxGadget::Syntax", "syntax");
 }
 

Some files were not shown because too many files changed in this diff