From 9c90fb6a7cbe60b1c22bbfc2286a8116cff68d8a Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Sun, 10 Dec 2023 09:07:21 +0100 Subject: [PATCH] Implement JSON (de)serialization of enums Implement the QProtobufJsonSerializer::serializeEnum, QProtobufJsonSerializer::serializeEnumList, QProtobufJsonSerializer::deserializeEnum and QProtobufJsonSerializer::deserializeEnumList functions. [ChangeLog][QtProtobuf][QProtobufBaseSerializer] Changed the following interfaces: - serializeEnum - serializeEnumList - deserializeEnum - deserializeEnumList All interfaces now have the mandatory const QMetaEnum &metaEnum argument. The argument is needed to convert the enum value to its string representation. Pick-to: 6.7 Task-number: QTBUG-113731 Change-Id: I72061fd303c217883cbbe053a7fdd68d089f4bf8 Reviewed-by: Tatiana Borisova --- src/protobuf/qprotobufbaseserializer.cpp | 16 ++++-- src/protobuf/qprotobufbaseserializer.h | 23 +++++--- src/protobuf/qprotobufjsonserializer.cpp | 47 ++++++++++++++++ src/protobuf/qprotobufjsonserializer.h | 29 +++------- src/protobuf/qprotobufserializer.cpp | 8 +-- src/protobuf/qprotobufserializer.h | 10 ++-- tests/auto/protobuf/jsontypes/CMakeLists.txt | 29 ++++++++++ ...rotobuf_deserialization_json_enumtypes.cpp | 53 ++++++++++++++++++ ..._protobuf_serialization_json_enumtypes.cpp | 54 +++++++++++++++++++ 9 files changed, 228 insertions(+), 41 deletions(-) create mode 100644 tests/auto/protobuf/jsontypes/tst_protobuf_deserialization_json_enumtypes.cpp create mode 100644 tests/auto/protobuf/jsontypes/tst_protobuf_serialization_json_enumtypes.cpp diff --git a/src/protobuf/qprotobufbaseserializer.cpp b/src/protobuf/qprotobufbaseserializer.cpp index 942d7e61..3f196161 100644 --- a/src/protobuf/qprotobufbaseserializer.cpp +++ b/src/protobuf/qprotobufbaseserializer.cpp @@ -104,9 +104,11 @@ QT_BEGIN_NAMESPACE /*! \fn void QProtobufBaseSerializer::serializeEnum(QtProtobuf::int64 value, + const QMetaEnum &metaEnum, const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const This function serializes \a value from enum associated with property \a fieldInfo. + \a metaEnum helps to encode the enum value. You should not call this function directly. @@ -114,9 +116,11 @@ QT_BEGIN_NAMESPACE */ /*! - \fn bool QProtobufBaseSerializer::deserializeEnum(QtProtobuf::int64 &value) const + \fn bool QProtobufBaseSerializer::deserializeEnum(QtProtobuf::int64 &value, + const QMetaEnum &metaEnum) const - This function deserializes an enum \a value from a wire. + This function deserializes an enum \a value from a wire. \a metaEnum helps to decode the enum + value. Returns \c true if deserialization was successful, otherwise \c false. You should not call this function directly. @@ -126,9 +130,11 @@ QT_BEGIN_NAMESPACE /*! \fn void QProtobufBaseSerializer::serializeEnumList(const QList &value, + const QMetaEnum &metaEnum, const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const This function serializes a list, \a value, for enum list associated with property \a fieldInfo. + \a metaEnum helps to encode the enum value. You should not call this function directly. @@ -136,9 +142,11 @@ QT_BEGIN_NAMESPACE */ /*! - \fn bool QProtobufSerializer::deserializeEnumList(QList &value) const + \fn bool QProtobufBaseSerializer::deserializeEnumList(QList &value, + const QMetaEnum &metaEnum) const - This function deserializes a list of enum \a value from a wire. + This function deserializes a list of enum \a value from a wire. \a metaEnum helps to decode + the enum value. Returns \c true if deserialization was successful, otherwise \c false. You should not call this function directly. diff --git a/src/protobuf/qprotobufbaseserializer.h b/src/protobuf/qprotobufbaseserializer.h index b9f248ef..1ad85616 100644 --- a/src/protobuf/qprotobufbaseserializer.h +++ b/src/protobuf/qprotobufbaseserializer.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -38,14 +39,15 @@ public: virtual bool deserializeMapPair(QVariant &key, QVariant &value) const = 0; virtual void - serializeEnum(QtProtobuf::int64 value, + serializeEnum(QtProtobuf::int64 value, const QMetaEnum &metaEnum, const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const = 0; virtual void - serializeEnumList(const QList &value, + serializeEnumList(const QList &value, const QMetaEnum &metaEnum, const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const = 0; - virtual bool deserializeEnum(QtProtobuf::int64 &value) const = 0; - virtual bool deserializeEnumList(QList &value) const = 0; + virtual bool deserializeEnum(QtProtobuf::int64 &value, const QMetaEnum &metaEnum) const = 0; + virtual bool deserializeEnumList(QList &value, + const QMetaEnum &metaEnum) const = 0; }; namespace QtProtobufPrivate { @@ -138,7 +140,9 @@ void serializeEnum(const QProtobufBaseSerializer *serializer, const QVariant &va const QProtobufPropertyOrderingInfo &fieldInfo) { Q_ASSERT_X(serializer != nullptr, "QProtobufBaseSerializer", "Serializer is null"); - serializer->serializeEnum(QtProtobuf::int64(value.value()), fieldInfo); + static const QMetaEnum metaEnum = QMetaEnum::fromType(); + serializer->serializeEnum(QtProtobuf::int64(value.value()), metaEnum, + fieldInfo); } /*! @@ -150,11 +154,12 @@ void serializeEnumList(const QProtobufBaseSerializer *serializer, const QVariant const QProtobufPropertyOrderingInfo &fieldInfo) { Q_ASSERT_X(serializer != nullptr, "QProtobufBaseSerializer", "Serializer is null"); + static const QMetaEnum metaEnum = QMetaEnum::fromType(); QList intList; for (auto enumValue : value.value>()) { intList.append(QtProtobuf::int64(enumValue)); } - serializer->serializeEnumList(intList, fieldInfo); + serializer->serializeEnumList(intList, metaEnum, fieldInfo); } /*! @@ -248,8 +253,9 @@ template ::value, int> = 0 void deserializeEnum(const QProtobufBaseSerializer *serializer, QVariant &to) { Q_ASSERT_X(serializer != nullptr, "QProtobufBaseSerializer", "Serializer is null"); + static const QMetaEnum metaEnum = QMetaEnum::fromType(); QtProtobuf::int64 intValue; - if (serializer->deserializeEnum(intValue)) + if (serializer->deserializeEnum(intValue, metaEnum)) to = QVariant::fromValue(static_cast(intValue._t)); } @@ -262,8 +268,9 @@ template ::value, int> = 0 void deserializeEnumList(const QProtobufBaseSerializer *serializer, QVariant &previous) { Q_ASSERT_X(serializer != nullptr, "QProtobufBaseSerializer", "Serializer is null"); + static const QMetaEnum metaEnum = QMetaEnum::fromType(); QList intList; - if (!serializer->deserializeEnumList(intList)) + if (!serializer->deserializeEnumList(intList, metaEnum)) return; QList enumList = previous.value>(); for (auto intValue : intList) diff --git a/src/protobuf/qprotobufjsonserializer.cpp b/src/protobuf/qprotobufjsonserializer.cpp index b197ba9d..94dd1ac0 100644 --- a/src/protobuf/qprotobufjsonserializer.cpp +++ b/src/protobuf/qprotobufjsonserializer.cpp @@ -715,4 +715,51 @@ bool QProtobufJsonSerializer::deserializeMapPair(QVariant &key, QVariant &value) return true; } +void QProtobufJsonSerializer::serializeEnum(QtProtobuf::int64 value, const QMetaEnum &metaEnum, + const QtProtobufPrivate::QProtobufPropertyOrderingInfo + &fieldInfo) const +{ + QJsonObject activeObject = d_ptr->activeValue.toObject(); + activeObject.insert(fieldInfo.getJsonName().toString(), QString::fromUtf8(metaEnum.key(value))); + d_ptr->activeValue = activeObject; +} + +void QProtobufJsonSerializer:: + serializeEnumList(const QList &values, const QMetaEnum &metaEnum, + const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const +{ + QJsonArray arr; + for (const auto value : values) + arr.append(QString::fromUtf8(metaEnum.key(value))); + + QJsonObject activeObject = d_ptr->activeValue.toObject(); + activeObject.insert(fieldInfo.getJsonName().toString(), arr); + d_ptr->activeValue = activeObject; +} + +bool QProtobufJsonSerializer::deserializeEnum(QtProtobuf::int64 &value, + const QMetaEnum &metaEnum) const +{ + QString enumKey = d_ptr->activeValue.toString(); + bool ok = false; + value = metaEnum.keyToValue(enumKey.toUtf8().data(), &ok); + d_ptr->activeValue = {}; + return ok; +} + +bool QProtobufJsonSerializer::deserializeEnumList(QList &value, + const QMetaEnum &metaEnum) const +{ + QJsonArray arr = d_ptr->activeValue.toArray(); + bool ok = false; + for (const auto &val : arr) { + QString enumKey = val.toString(); + value.append(metaEnum.keyToValue(enumKey.toUtf8().data(), &ok)); + if (!ok) + break; + } + d_ptr->activeValue = {}; + return ok; +} + QT_END_NAMESPACE diff --git a/src/protobuf/qprotobufjsonserializer.h b/src/protobuf/qprotobufjsonserializer.h index 4564fef0..460e7a4e 100644 --- a/src/protobuf/qprotobufjsonserializer.h +++ b/src/protobuf/qprotobufjsonserializer.h @@ -59,32 +59,17 @@ private: bool deserializeMapPair(QVariant &key, QVariant &value) const override; void - serializeEnum(QtProtobuf::int64 value, - const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const override - { - Q_UNUSED(value); - Q_UNUSED(fieldInfo); - } + serializeEnum(QtProtobuf::int64 value, const QMetaEnum &metaEnum, + const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const override; - void serializeEnumList(const QList &value, + void serializeEnumList(const QList &value, const QMetaEnum &metaEnum, const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) - const override - { - Q_UNUSED(value); - Q_UNUSED(fieldInfo); - } + const override; - bool deserializeEnum(QtProtobuf::int64 &value) const override - { - Q_UNUSED(value); - return false; - } + bool deserializeEnum(QtProtobuf::int64 &value, const QMetaEnum &metaEnum) const override; - bool deserializeEnumList(QList &value) const override - { - Q_UNUSED(value); - return false; - } + bool deserializeEnumList(QList &value, + const QMetaEnum &metaEnum) const override; private: std::unique_ptr d_ptr; diff --git a/src/protobuf/qprotobufserializer.cpp b/src/protobuf/qprotobufserializer.cpp index 5bb1187c..b21eebc7 100644 --- a/src/protobuf/qprotobufserializer.cpp +++ b/src/protobuf/qprotobufserializer.cpp @@ -387,7 +387,7 @@ bool QProtobufSerializer::deserializeMapPair(QVariant &key, QVariant &value) con return d_ptr->deserializeMapPair(key, value); } -void QProtobufSerializer::serializeEnum(QtProtobuf::int64 value, +void QProtobufSerializer::serializeEnum(QtProtobuf::int64 value, const QMetaEnum &, const QProtobufPropertyOrderingInfo &fieldInfo) const { if (value == 0 && !isOneofOrOptionalField(fieldInfo)) @@ -400,6 +400,7 @@ void QProtobufSerializer::serializeEnum(QtProtobuf::int64 value, } void QProtobufSerializer::serializeEnumList(const QList &value, + const QMetaEnum &, const QProtobufPropertyOrderingInfo &fieldInfo) const { if (value.isEmpty()) @@ -418,7 +419,7 @@ void QProtobufSerializer::serializeEnumList(const QList &valu + QProtobufSerializerPrivate::serializeListType(value)); } -bool QProtobufSerializer::deserializeEnum(QtProtobuf::int64 &value) const +bool QProtobufSerializer::deserializeEnum(QtProtobuf::int64 &value, const QMetaEnum &) const { QVariant variantValue; if (!QProtobufSerializerPrivate::deserializeBasic(d_ptr->it, variantValue)) { @@ -429,7 +430,8 @@ bool QProtobufSerializer::deserializeEnum(QtProtobuf::int64 &value) const return true; } -bool QProtobufSerializer::deserializeEnumList(QList &value) const +bool QProtobufSerializer::deserializeEnumList(QList &value, + const QMetaEnum &) const { QVariant variantValue; if (!QProtobufSerializerPrivate::deserializeList(d_ptr->it, variantValue)) { diff --git a/src/protobuf/qprotobufserializer.h b/src/protobuf/qprotobufserializer.h index 45806879..46ec23da 100644 --- a/src/protobuf/qprotobufserializer.h +++ b/src/protobuf/qprotobufserializer.h @@ -58,14 +58,16 @@ private: bool deserializeMapPair(QVariant &key, QVariant &value) const override; void - serializeEnum(QtProtobuf::int64 value, + serializeEnum(QtProtobuf::int64 value, const QMetaEnum &metaEnum, const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const override; - void serializeEnumList(const QList &value, + void serializeEnumList(const QList &value, const QMetaEnum &metaEnum, const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const override; - bool deserializeEnum(QtProtobuf::int64 &value) const override; - bool deserializeEnumList(QList &value) const override; + bool deserializeEnum(QtProtobuf::int64 &value, const QMetaEnum &metaEnum) const override; + bool deserializeEnumList(QList &value, + const QMetaEnum &metaEnum) const override; + private: std::unique_ptr d_ptr; }; diff --git a/tests/auto/protobuf/jsontypes/CMakeLists.txt b/tests/auto/protobuf/jsontypes/CMakeLists.txt index 90155952..d795e137 100644 --- a/tests/auto/protobuf/jsontypes/CMakeLists.txt +++ b/tests/auto/protobuf/jsontypes/CMakeLists.txt @@ -102,3 +102,32 @@ qt_internal_add_test(tst_protobuf_deserialization_json_maptypes tst_protobuf_json_basic_types_gen tst_protobuf_json_maptypes_gen ) + +qt6_add_protobuf(tst_protobuf_json_enumtypes_gen + PROTO_FILES + ../enums/enummessages.proto + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/qt_protobuf_generated" +) +qt_autogen_tools_initial_setup(tst_protobuf_json_enumtypes_gen) + +qt_internal_add_test(tst_protobuf_serialization_json_enumtypes + SOURCES + tst_protobuf_serialization_json_enumtypes.cpp + INCLUDE_DIRECTORIES + ../shared + LIBRARIES + Qt::Test + Qt::Protobuf + tst_protobuf_json_enumtypes_gen +) + +qt_internal_add_test(tst_protobuf_deserialization_json_enumtypes + SOURCES + tst_protobuf_deserialization_json_enumtypes.cpp + INCLUDE_DIRECTORIES + ../shared + LIBRARIES + Qt::Test + Qt::Protobuf + tst_protobuf_json_enumtypes_gen +) diff --git a/tests/auto/protobuf/jsontypes/tst_protobuf_deserialization_json_enumtypes.cpp b/tests/auto/protobuf/jsontypes/tst_protobuf_deserialization_json_enumtypes.cpp new file mode 100644 index 00000000..de30d352 --- /dev/null +++ b/tests/auto/protobuf/jsontypes/tst_protobuf_deserialization_json_enumtypes.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "enummessages.qpb.h" + +#include +#include + +class QtProtobufEnumTypesDeserializationTest : public QObject +{ + Q_OBJECT +private slots: + void init() { m_serializer.reset(new QProtobufJsonSerializer); } + void SimpleEnumMessageDeserializeTest(); + void RepeatedEnumMessageTest(); + +private: + std::unique_ptr m_serializer; +}; + +using namespace qtprotobufnamespace::tests; + +void QtProtobufEnumTypesDeserializationTest::SimpleEnumMessageDeserializeTest() +{ + SimpleEnumMessage test; + test.deserialize(m_serializer.get(), "{\"localEnum\":\"LOCAL_ENUM_VALUE2\"}"); + QCOMPARE(test.localEnum(), SimpleEnumMessage::LocalEnum::LOCAL_ENUM_VALUE2); +} + +void QtProtobufEnumTypesDeserializationTest::RepeatedEnumMessageTest() +{ + RepeatedEnumMessage msg; + + msg.deserialize(m_serializer.get(), QByteArray()); + QVERIFY(msg.localEnumList().isEmpty()); + + msg.deserialize(m_serializer.get(), + "{\"localEnumList\":[\"LOCAL_ENUM_VALUE0\"," + "\"LOCAL_ENUM_VALUE1\",\"LOCAL_ENUM_VALUE2\"," + "\"LOCAL_ENUM_VALUE1\",\"LOCAL_ENUM_VALUE2\"," + "\"LOCAL_ENUM_VALUE3\"]}"); + QVERIFY((msg.localEnumList() + == RepeatedEnumMessage::LocalEnumRepeated{ + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE0, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE1, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE2, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE1, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE2, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE3 })); +} + +QTEST_MAIN(QtProtobufEnumTypesDeserializationTest) +#include "tst_protobuf_deserialization_json_enumtypes.moc" diff --git a/tests/auto/protobuf/jsontypes/tst_protobuf_serialization_json_enumtypes.cpp b/tests/auto/protobuf/jsontypes/tst_protobuf_serialization_json_enumtypes.cpp new file mode 100644 index 00000000..4ee7fac7 --- /dev/null +++ b/tests/auto/protobuf/jsontypes/tst_protobuf_serialization_json_enumtypes.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "enummessages.qpb.h" + +#include +#include + +class QtProtobufEnumTypesJsonSerializationTest : public QObject +{ + Q_OBJECT +private slots: + void SimpleEnumMessageSerializeTest(); + void RepeatedEnumMessageTest(); + + void init() { m_serializer.reset(new QProtobufJsonSerializer); } + +private: + std::unique_ptr m_serializer; +}; + +using namespace qtprotobufnamespace::tests; +using namespace Qt::Literals::StringLiterals; + +void QtProtobufEnumTypesJsonSerializationTest::SimpleEnumMessageSerializeTest() +{ + SimpleEnumMessage test; + test.setLocalEnum(SimpleEnumMessage::LocalEnum::LOCAL_ENUM_VALUE2); + QByteArray result = test.serialize(m_serializer.get()); + QCOMPARE(result, "{\"localEnum\":\"LOCAL_ENUM_VALUE2\"}"_ba); +} + +void QtProtobufEnumTypesJsonSerializationTest::RepeatedEnumMessageTest() +{ + RepeatedEnumMessage msg; + + msg.setLocalEnumList({ RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE0, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE1, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE2, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE1, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE2, + RepeatedEnumMessage::LocalEnum::LOCAL_ENUM_VALUE3 }); + QByteArray result = msg.serialize(m_serializer.get()); + QCOMPARE(result, + "{\"localEnumList\":[\"LOCAL_ENUM_VALUE0\",\"LOCAL_ENUM_VALUE1\"," + "\"LOCAL_ENUM_VALUE2\",\"LOCAL_ENUM_VALUE1\",\"LOCAL_ENUM_VALUE2\"," + "\"LOCAL_ENUM_VALUE3\"]}"_ba); + msg.setLocalEnumList({}); + result = msg.serialize(m_serializer.get()); + QCOMPARE(result, "{\"localEnumList\":[]}"_ba); +} + +QTEST_MAIN(QtProtobufEnumTypesJsonSerializationTest) +#include "tst_protobuf_serialization_json_enumtypes.moc"