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 <tatiana.borisova@qt.io>
This commit is contained in:
Alexey Edelev 2023-12-10 09:07:21 +01:00
parent 6d69e9972e
commit 9c90fb6a7c
9 changed files with 228 additions and 41 deletions

View File

@ -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<QtProtobuf::int64> &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<QtProtobuf::int64> &value) const
\fn bool QProtobufBaseSerializer::deserializeEnumList(QList<QtProtobuf::int64> &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.

View File

@ -7,6 +7,7 @@
#include <QtProtobuf/qabstractprotobufserializer.h>
#include <QtCore/QList>
#include <QtCore/QMetaEnum>
#include <QtCore/QMetaObject>
#include <QtCore/QPair>
#include <QtCore/QVariant>
@ -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<QtProtobuf::int64> &value,
serializeEnumList(const QList<QtProtobuf::int64> &value, const QMetaEnum &metaEnum,
const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const = 0;
virtual bool deserializeEnum(QtProtobuf::int64 &value) const = 0;
virtual bool deserializeEnumList(QList<QtProtobuf::int64> &value) const = 0;
virtual bool deserializeEnum(QtProtobuf::int64 &value, const QMetaEnum &metaEnum) const = 0;
virtual bool deserializeEnumList(QList<QtProtobuf::int64> &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<T>()), fieldInfo);
static const QMetaEnum metaEnum = QMetaEnum::fromType<T>();
serializer->serializeEnum(QtProtobuf::int64(value.value<T>()), 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<T>();
QList<QtProtobuf::int64> intList;
for (auto enumValue : value.value<QList<T>>()) {
intList.append(QtProtobuf::int64(enumValue));
}
serializer->serializeEnumList(intList, fieldInfo);
serializer->serializeEnumList(intList, metaEnum, fieldInfo);
}
/*!
@ -248,8 +253,9 @@ template <typename T, typename std::enable_if_t<std::is_enum<T>::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<T>();
QtProtobuf::int64 intValue;
if (serializer->deserializeEnum(intValue))
if (serializer->deserializeEnum(intValue, metaEnum))
to = QVariant::fromValue<T>(static_cast<T>(intValue._t));
}
@ -262,8 +268,9 @@ template <typename T, typename std::enable_if_t<std::is_enum<T>::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<T>();
QList<QtProtobuf::int64> intList;
if (!serializer->deserializeEnumList(intList))
if (!serializer->deserializeEnumList(intList, metaEnum))
return;
QList<T> enumList = previous.value<QList<T>>();
for (auto intValue : intList)

View File

@ -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<QtProtobuf::int64> &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<QtProtobuf::int64> &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

View File

@ -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<QtProtobuf::int64> &value,
void serializeEnumList(const QList<QtProtobuf::int64> &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<QtProtobuf::int64> &value) const override
{
Q_UNUSED(value);
return false;
}
bool deserializeEnumList(QList<QtProtobuf::int64> &value,
const QMetaEnum &metaEnum) const override;
private:
std::unique_ptr<QProtobufJsonSerializerPrivate> d_ptr;

View File

@ -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<QtProtobuf::int64> &value,
const QMetaEnum &,
const QProtobufPropertyOrderingInfo &fieldInfo) const
{
if (value.isEmpty())
@ -418,7 +419,7 @@ void QProtobufSerializer::serializeEnumList(const QList<QtProtobuf::int64> &valu
+ QProtobufSerializerPrivate::serializeListType<QtProtobuf::int64>(value));
}
bool QProtobufSerializer::deserializeEnum(QtProtobuf::int64 &value) const
bool QProtobufSerializer::deserializeEnum(QtProtobuf::int64 &value, const QMetaEnum &) const
{
QVariant variantValue;
if (!QProtobufSerializerPrivate::deserializeBasic<QtProtobuf::int64>(d_ptr->it, variantValue)) {
@ -429,7 +430,8 @@ bool QProtobufSerializer::deserializeEnum(QtProtobuf::int64 &value) const
return true;
}
bool QProtobufSerializer::deserializeEnumList(QList<QtProtobuf::int64> &value) const
bool QProtobufSerializer::deserializeEnumList(QList<QtProtobuf::int64> &value,
const QMetaEnum &) const
{
QVariant variantValue;
if (!QProtobufSerializerPrivate::deserializeList<QtProtobuf::int64>(d_ptr->it, variantValue)) {

View File

@ -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<QtProtobuf::int64> &value,
void serializeEnumList(const QList<QtProtobuf::int64> &value, const QMetaEnum &metaEnum,
const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo)
const override;
bool deserializeEnum(QtProtobuf::int64 &value) const override;
bool deserializeEnumList(QList<QtProtobuf::int64> &value) const override;
bool deserializeEnum(QtProtobuf::int64 &value, const QMetaEnum &metaEnum) const override;
bool deserializeEnumList(QList<QtProtobuf::int64> &value,
const QMetaEnum &metaEnum) const override;
private:
std::unique_ptr<QProtobufSerializerPrivate> d_ptr;
};

View File

@ -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
)

View File

@ -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 <QProtobufJsonSerializer>
#include <QTest>
class QtProtobufEnumTypesDeserializationTest : public QObject
{
Q_OBJECT
private slots:
void init() { m_serializer.reset(new QProtobufJsonSerializer); }
void SimpleEnumMessageDeserializeTest();
void RepeatedEnumMessageTest();
private:
std::unique_ptr<QProtobufJsonSerializer> 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"

View File

@ -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 <QProtobufJsonSerializer>
#include <QTest>
class QtProtobufEnumTypesJsonSerializationTest : public QObject
{
Q_OBJECT
private slots:
void SimpleEnumMessageSerializeTest();
void RepeatedEnumMessageTest();
void init() { m_serializer.reset(new QProtobufJsonSerializer); }
private:
std::unique_ptr<QProtobufJsonSerializer> 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"