Generalize deserializer in protobuf and JSON serializers

Move the generic logic of serializer to a separate class and
use this class to implement the protobuf and JSON deserialization.

This also fixed one of the conformance usecases since related
to invalid enum handling.

Pick-to: 6.8 6.9
Task-number: QTBUG-128812
Fixes: QTBUG-112423
Fixes: QTBUG-112425
Fixes: QTBUG-112424
Change-Id: I400303a8666df90050a54bd7036daa0107adcce5
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
This commit is contained in:
Alexey Edelev 2024-09-27 12:55:53 +02:00
parent 27b618fcc4
commit 3df9d285da
8 changed files with 680 additions and 610 deletions

View File

@ -6,6 +6,7 @@ qt_internal_add_module(Protobuf
protobuffieldpresencechecker_p.h
qtprotobufglobal.h
qabstractprotobufserializer.cpp qabstractprotobufserializer.h
qprotobufdeserializerbase_p.h qprotobufdeserializerbase.cpp
qprotobufjsonserializer.cpp qprotobufjsonserializer.h
qprotobuflazymessagepointer.h
qprotobufmessage.cpp qprotobufmessage.h qprotobufmessage_p.h

View File

@ -0,0 +1,136 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
#include <QtProtobuf/private/qprotobufregistration_p.h>
#include <QtProtobuf/private/qtprotobuflogging_p.h>
#include <QtProtobuf/private/qtprotobufserializerhelpers_p.h>
QT_BEGIN_NAMESPACE
QProtobufDeserializerBase::QProtobufDeserializerBase()
= default;
QProtobufDeserializerBase::~QProtobufDeserializerBase()
= default;
bool QProtobufDeserializerBase::deserializeMessageField(QProtobufMessage *message)
{
Q_ASSERT(message != nullptr);
auto prevCachedRepeatedIterator = std::move(m_cachedRepeatedIterator);
auto prevCachedPropertyValue = m_cachedPropertyValue;
auto prevCachedIndex = m_cachedIndex;
clearCachedValue();
bool result = deserializeMessage(message);
m_cachedPropertyValue = prevCachedPropertyValue;
m_cachedIndex = prevCachedIndex;
m_cachedRepeatedIterator = std::move(prevCachedRepeatedIterator);
return result;
}
bool QProtobufDeserializerBase::deserializeMessage(QProtobufMessage *message)
{
Q_ASSERT(message != nullptr);
const auto *ordering = message->propertyOrdering();
for (int fieldIndex = nextFieldIndex(message); fieldIndex >= 0;
fieldIndex = nextFieldIndex(message)) {
QtProtobufPrivate::QProtobufFieldInfo fieldInfo(*ordering, fieldIndex);
if (m_cachedIndex != fieldIndex) {
if (!storeCachedValue(message)) {
setError(QAbstractProtobufSerializer::Error::InvalidFormat,
"Unable to store the property in message");
return false;
}
m_cachedPropertyValue = QtProtobufSerializerHelpers::messageProperty(message, fieldInfo,
true);
m_cachedIndex = fieldIndex;
}
QMetaType metaType = m_cachedPropertyValue.metaType();
if (metaType.flags().testFlag(QMetaType::IsPointer)) {
if (!deserializeMessageField(m_cachedPropertyValue.value<QProtobufMessage *>()))
return false;
continue;
}
const auto fieldFlags = fieldInfo.fieldFlags();
if ((fieldFlags.testFlags(RepeatedMessageFlags)
|| fieldFlags.testFlags({ QtProtobufPrivate::FieldFlag::Map }))
&& m_cachedPropertyValue.canView(QMetaType::fromType<QProtobufRepeatedIterator>())) {
if (!m_cachedRepeatedIterator.isValid())
m_cachedRepeatedIterator = m_cachedPropertyValue.view<QProtobufRepeatedIterator>();
if (!deserializeMessageField(m_cachedRepeatedIterator.addNext()))
return false;
m_cachedRepeatedIterator.push();
continue;
}
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Enum)) {
if (!deserializeEnum(m_cachedPropertyValue, fieldInfo)) {
setError(QAbstractProtobufSerializer::Error::UnknownType,
"Unable to covert enum field to compatible serialization format");
return false;
}
continue;
}
if (deserializeScalarField(m_cachedPropertyValue, fieldInfo)) {
if (!m_cachedPropertyValue.isValid())
return false;
continue;
}
auto handler = QtProtobufPrivate::findHandler(metaType);
if (!handler.deserializer) {
qProtoWarning() << "No deserializer for type" << metaType.name();
setError(QAbstractProtobufSerializer::Error::UnknownType,
QString::fromUtf8("No deserializer is registered for type %1")
.arg(QString::fromUtf8(metaType.name())));
return false;
}
handler.deserializer([this](QProtobufMessage *
message) { return this->deserializeMessageField(message); },
m_cachedPropertyValue.data());
}
if (!storeCachedValue(message)) {
setError(QAbstractProtobufSerializer::Error::InvalidFormat,
"Unable to store the property in message");
return false;
}
return true;
}
bool QProtobufDeserializerBase::storeCachedValue(QProtobufMessage *message)
{
bool ok = true;
if (m_cachedIndex >= 0 && !m_cachedPropertyValue.isNull()) {
const auto *ordering = message->propertyOrdering();
QtProtobufPrivate::QProtobufFieldInfo fieldInfo(*ordering, m_cachedIndex);
ok = QtProtobufSerializerHelpers::setMessageProperty(message, fieldInfo,
m_cachedPropertyValue);
clearCachedValue();
}
return ok;
}
void QProtobufDeserializerBase::clearCachedValue()
{
m_cachedPropertyValue.clear();
m_cachedIndex = -1;
m_cachedRepeatedIterator = {};
}
QT_END_NAMESPACE

View File

@ -0,0 +1,65 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPROTOBUFDESERIALIZERBASE_P_H
#define QPROTOBUFDESERIALIZERBASE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtProtobuf/qabstractprotobufserializer.h>
#include <QtProtobuf/qprotobufpropertyordering.h>
#include <QtProtobuf/qprotobufrepeatediterator.h>
#include <QtCore/qtconfigmacros.h>
QT_BEGIN_NAMESPACE
class QVariant;
class QProtobufMessage;
class QProtobufDeserializerBase
{
public:
QProtobufDeserializerBase();
bool deserializeMessage(QProtobufMessage *message);
void clearCachedValue();
protected:
~QProtobufDeserializerBase();
virtual void setError(QAbstractProtobufSerializer::Error error, QAnyStringView errorString) = 0;
private:
virtual bool deserializeEnum(QVariant &value,
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) = 0;
virtual int nextFieldIndex(QProtobufMessage *message) = 0;
virtual bool deserializeScalarField(QVariant &value,
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) = 0;
bool deserializeMessageField(QProtobufMessage *message);
bool storeCachedValue(QProtobufMessage *message);
QVariant m_cachedPropertyValue;
QProtobufRepeatedIterator m_cachedRepeatedIterator;
int m_cachedIndex = -1;
Q_DISABLE_COPY_MOVE(QProtobufDeserializerBase)
public:
static constexpr QtProtobufPrivate::FieldFlags RepeatedMessageFlags{
QtProtobufPrivate::FieldFlag::Message, QtProtobufPrivate::FieldFlag::Repeated
};
};
QT_END_NAMESPACE
#endif // QPROTOBUFDESERIALIZERBASE_P_H

View File

@ -4,6 +4,7 @@
#include <QtProtobuf/qprotobufjsonserializer.h>
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
#include <QtProtobuf/private/qprotobufregistration_p.h>
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
#include <QtProtobuf/private/qtprotobufdefs_p.h>
@ -19,7 +20,6 @@
#include <cmath>
#include <limits>
#include <map>
#include <type_traits>
QT_BEGIN_NAMESPACE
@ -85,6 +85,38 @@ private:
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerImpl)
};
class QProtobufJsonSerializerPrivate;
class QProtobufJsonDeserializerImpl final : public QProtobufDeserializerBase
{
public:
explicit QProtobufJsonDeserializerImpl(QProtobufJsonSerializerPrivate *parent);
~QProtobufJsonDeserializerImpl();
void reset(QJsonObject obj);
void setError(QAbstractProtobufSerializer::Error error, QAnyStringView errorString) override;
void setUnexpectedEndOfStreamError();
void setInvalidFormatError();
private:
bool deserializeEnum(QVariant &value,
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) override;
int nextFieldIndex(QProtobufMessage *message) override;
bool deserializeScalarField(QVariant &, const QtProtobufPrivate::QProtobufFieldInfo &) override;
struct JsonDeserializerState
{
JsonDeserializerState(const QJsonObject &obj) : obj(obj) { }
QJsonObject obj = {};
int index = 0;
QJsonValue scalarValue = {};
};
QList<JsonDeserializerState> m_state;
QProtobufJsonSerializerPrivate *m_parent = nullptr;
};
class QProtobufJsonSerializerPrivate final
{
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
@ -211,7 +243,7 @@ public:
return QJsonValue(arr);
}
QProtobufJsonSerializerPrivate()
QProtobufJsonSerializerPrivate() : deserializer(this)
{
[[maybe_unused]] static bool initialized = []() -> bool {
handlers[qMetaTypeId<QtProtobuf::int32>()] = createCommonHandler<QtProtobuf::int32>();
@ -274,17 +306,6 @@ public:
}
~QProtobufJsonSerializerPrivate() = default;
[[nodiscard]] static QMetaEnum getMetaEnum(QMetaType enumMetaType)
{
const auto *metaObject = enumMetaType.metaObject();
Q_ASSERT(metaObject);
for (int i = 0; i < metaObject->enumeratorCount(); ++i) {
if (metaObject->enumerator(i).metaType() == enumMetaType)
return metaObject->enumerator(i);
}
return {};
}
template <typename T>
static QVariant deserializeCommon(const QJsonValue &value, bool &ok)
{
@ -427,236 +448,15 @@ public:
return QVariant::fromValue(list);
}
static QtProtobuf::int64 deserializeEnum(const QJsonValue &value, const QMetaEnum &metaEnum,
bool &ok)
{
QtProtobuf::int64 result = 0;
if (value.isString()) {
QString enumKey = value.toString();
result = metaEnum.keyToValue(enumKey.toUtf8().data(), &ok);
}
if (ok)
return result;
result = deserialize<QtProtobuf::int64>(value, ok);
if (ok) {
ok = false;
// Make sure that it's the known enum value
for (int i = 0; i < metaEnum.keyCount(); ++i) {
if (metaEnum.value(i) == result) {
ok = true;
break;
}
}
}
return result;
}
QVariant deserializeValue(QVariant propertyData, bool &ok)
{
ok = false;
auto metaType = propertyData.metaType();
if (metaType.flags() & QMetaType::IsPointer) {
auto *messageProperty = propertyData.value<QProtobufMessage *>();
Q_ASSERT(messageProperty != nullptr);
ok = deserializeObject(messageProperty);
return propertyData;
}
if (propertyData.canView(QMetaType::fromType<QProtobufRepeatedIterator>())) {
QProtobufRepeatedIterator propertyIt = propertyData.view<QProtobufRepeatedIterator>();
if (activeValue.isArray()) {
QJsonArray array = activeValue.toArray();
if (array.isEmpty()) {
ok = true;
activeValue = {};
return propertyData;
}
while (!array.isEmpty()
&& lastError == QAbstractProtobufSerializer::Error::None) {
activeValue = array.takeAt(0);
if (deserializeObject(propertyIt.addNext()))
propertyIt.push();
}
ok = propertyData.isValid();
} else {
while (!activeValue.isNull()
&& lastError == QAbstractProtobufSerializer::Error::None) {
if (deserializeObject(propertyIt.addNext()))
propertyIt.push();
}
}
ok = lastError == QAbstractProtobufSerializer::Error::None;
return propertyData;
}
auto handler = QtProtobufPrivate::findHandler(metaType);
if (handler.deserializer) {
while (!activeValue.isNull()
&& lastError == QAbstractProtobufSerializer::Error::None) {
handler
.deserializer([this](QProtobufMessage
*message) { return this->deserializeObject(message); },
propertyData.data());
}
ok = propertyData.isValid();
} else {
int userType = propertyData.userType();
auto handler = handlers.constFind(userType);
if (handler != handlers.constEnd() && handler.value().deserializer) {
propertyData = handler.value().deserializer(activeValue, ok);
if (!ok)
setInvalidFormatError();
} else {
setDeserializationError(QAbstractProtobufSerializer::Error::UnknownType,
QCoreApplication::
translate("QtProtobuf",
"No deserializer is registered for type %1")
.arg(userType));
}
}
return propertyData;
}
bool deserializeObject(QProtobufMessage *message)
{
Q_ASSERT(message != nullptr);
auto restoreOnReturn = qScopeGuard([prevCachedPropertyValue = cachedPropertyValue,
prevCachedIndex = cachedIndex, this]() {
cachedPropertyValue = prevCachedPropertyValue;
cachedIndex = prevCachedIndex;
});
cachedPropertyValue.clear();
cachedIndex = -1;
auto ordering = message->propertyOrdering();
Q_ASSERT(ordering != nullptr);
std::map<QString, QProtobufFieldInfo> msgContainer; // map<key, fieldInfo>
for (int index = 0; index < ordering->fieldCount(); ++index) {
int fieldIndex = ordering->fieldNumber(index);
Q_ASSERT_X(fieldIndex <= ProtobufFieldNumMax && fieldIndex >= ProtobufFieldNumMin, "",
"fieldIndex is out of range");
QProtobufFieldInfo fieldInfo(*ordering, index);
QString key = fieldInfo.jsonName().toString();
msgContainer.insert(std::pair<QString, QProtobufFieldInfo>(key, fieldInfo));
}
if (!activeValue.isObject()) {
setInvalidFormatError();
activeValue = {};
return false;
}
QJsonObject activeObject = activeValue.toObject();
// Go through QJSON doc and find keys that are presented in msgContainer
for (auto &key : activeObject.keys()) {
if (activeObject.value(key).isNull())
continue;
std::map<QString, QProtobufFieldInfo>::iterator iter = msgContainer
.find(key);
if (iter == msgContainer.end())
iter = msgContainer.find(convertJsonKeyToJsonName(key));
if (iter != msgContainer.end()) {
auto store = activeValue;
activeValue = activeObject.value(key);
if (auto index = ordering->indexOfFieldNumber(iter->second.fieldNumber());
index != cachedIndex) {
if (!storeCachedValue(message))
return false;
cachedPropertyValue = QtProtobufSerializerHelpers::messageProperty(message,
iter->second,
true);
cachedIndex = index;
}
bool ok = false;
const auto fieldFlags = iter->second.fieldFlags();
if (fieldFlags & QtProtobufPrivate::FieldFlag::Enum) {
if (fieldFlags & QtProtobufPrivate::FieldFlag::Repeated) {
QMetaType originalMetatype = cachedPropertyValue.metaType();
cachedPropertyValue
.setValue(deserializeList<QStringList, QString>(activeValue, ok));
if (ok)
ok = cachedPropertyValue.convert(originalMetatype);
} else {
const auto metaEnum = getMetaEnum(cachedPropertyValue.metaType());
Q_ASSERT(metaEnum.isValid());
cachedPropertyValue.setValue(deserializeEnum(activeValue, metaEnum, ok));
}
if (!ok)
setInvalidFormatError();
} else if (fieldFlags & QtProtobufPrivate::FieldFlag::Map) {
auto activeValueObj = activeValue.toObject();
for (auto it = activeValueObj.begin(); it != activeValueObj.end(); ++it) {
auto mapObj = QJsonObject{};
mapObj.insert("key"_L1, it.key());
mapObj.insert("value"_L1, it.value());
activeValue = mapObj;
cachedPropertyValue = deserializeValue(cachedPropertyValue, ok);
}
activeValue = store;
} else {
cachedPropertyValue = deserializeValue(cachedPropertyValue, ok);
activeValue = store;
}
if (!ok) {
cachedPropertyValue.clear();
cachedIndex = -1;
}
}
}
// Once all keys are deserialized we assume that activeValue is empty, nothing left
// to deserialize
activeValue = {};
return storeCachedValue(message);
}
void setDeserializationError(QAbstractProtobufSerializer::Error error,
const QString &errorString)
{
lastError = error;
lastErrorString = errorString;
}
void setUnexpectedEndOfStreamError()
{
setDeserializationError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
QCoreApplication::translate("QtProtobuf",
"JSON: Unexpected end of stream"));
}
void setInvalidFormatError()
{
setDeserializationError(QAbstractProtobufSerializer::Error::InvalidFormat,
QCoreApplication::
translate("QtProtobuf",
"JSON: One or more fields have invalid format"));
}
void clearError();
QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
QString lastErrorString;
QJsonValue activeValue;
static SerializerRegistry handlers;
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
QVariant cachedPropertyValue;
int cachedIndex = -1;
QProtobufJsonSerializerImpl serializer;
QProtobufJsonDeserializerImpl deserializer;
};
QProtobufJsonSerializerImpl::~QProtobufJsonSerializerImpl() = default;
@ -741,20 +541,171 @@ void QProtobufJsonSerializerImpl::serializeMessageFieldEnd(const QProtobufMessag
m_result = store;
}
bool QProtobufJsonSerializerPrivate::storeCachedValue(QProtobufMessage *message)
QProtobufJsonDeserializerImpl::QProtobufJsonDeserializerImpl(QProtobufJsonSerializerPrivate *parent)
: m_parent(parent)
{
bool ok = true;
if (cachedIndex >= 0 && !cachedPropertyValue.isNull()) {
const auto *ordering = message->propertyOrdering();
QProtobufFieldInfo fieldInfo(*ordering, cachedIndex);
ok = QtProtobufSerializerHelpers::setMessageProperty(message, fieldInfo,
cachedPropertyValue);
cachedPropertyValue.clear();
cachedIndex = -1;
}
QProtobufJsonDeserializerImpl::~QProtobufJsonDeserializerImpl()
= default;
void QProtobufJsonDeserializerImpl::reset(QJsonObject obj)
{
m_state.clear();
if (!obj.isEmpty())
m_state.push_back({ obj });
}
void QProtobufJsonDeserializerImpl::setError(QAbstractProtobufSerializer::Error error,
QAnyStringView errorString)
{
m_parent->lastError = error;
m_parent->lastErrorString = errorString.toString();
}
bool QProtobufJsonDeserializerImpl::deserializeEnum(QVariant &value,
const QtProtobufPrivate::QProtobufFieldInfo
&fieldInfo)
{
bool ok = false;
auto &state = m_state.last();
if (fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
value = QProtobufJsonSerializerPrivate::deserializeList<QStringList,
QString>(state.scalarValue, ok);
} else {
// It's allowed to pass single enum value as numeric value.
// Make the backward value conversion and deserialize enum as QtProtobuf::int64.
if (state.scalarValue.isString()) {
value = QProtobufJsonSerializerPrivate::deserializeCommon<QString>(state.scalarValue,
ok);
} else {
value = QProtobufJsonSerializerPrivate::deserializeCommon<
QtProtobuf::int64>(state.scalarValue, ok);
}
}
return ok;
}
int QProtobufJsonDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
{
const auto *ordering = message->propertyOrdering();
const int fieldCount = ordering->fieldCount();
if (fieldCount == 0)
return -1;
JsonDeserializerState &state = m_state.last();
state.scalarValue = {};
while (state.index < fieldCount) {
const auto jsonName = ordering->jsonName(state.index);
const auto keys = state.obj.keys();
const auto it = std::find_if(keys.constBegin(), keys.constEnd(),
[&jsonName](const auto &val) {
return jsonName == val
|| jsonName == convertJsonKeyToJsonName(val);
});
if (it == keys.constEnd()) {
++state.index;
continue;
}
QtProtobufPrivate::FieldFlags flags = ordering->fieldFlags(state.index);
QJsonValue val = state.obj.value(*it);
if (val.isNull()) {
++state.index;
continue;
}
int index = state.index;
if (flags.testFlags({ QtProtobufPrivate::FieldFlag::Message,
QtProtobufPrivate::FieldFlag::Repeated })) {
if (!val.isArray()) {
setInvalidFormatError();
return -1;
}
auto array = val.toArray();
if (array.isEmpty()) {
++state.index;
continue;
}
if (!array.at(0).isObject()) {
setInvalidFormatError();
return -1;
}
auto nextObject = array.takeAt(0).toObject();
state.obj.insert(*it, array);
m_state.push_back({ nextObject });
} else if (flags.testFlag(QtProtobufPrivate::FieldFlag::Map)) {
if (!val.isObject()) {
setInvalidFormatError();
return -1;
}
auto mapObject = val.toObject();
if (mapObject.isEmpty()) {
++state.index;
continue;
}
QString key = mapObject.begin().key();
QJsonObject nextObject;
nextObject.insert("key"_L1, key);
nextObject.insert("value"_L1, mapObject.take(key));
state.obj.insert(*it, mapObject);
m_state.push_back({ nextObject });
} else if (flags.testFlag(QtProtobufPrivate::FieldFlag::Message)) {
if (!val.isObject()) {
setInvalidFormatError();
return -1;
}
auto nextObject = val.toObject();
++state.index;
m_state.push_back({ nextObject });
} else {
state.scalarValue = val;
++state.index;
}
return index;
}
m_state.pop_back();
return -1;
}
bool QProtobufJsonDeserializerImpl::deserializeScalarField(QVariant &value,
const QProtobufFieldInfo &)
{
auto handler = QProtobufJsonSerializerPrivate::handlers.constFind(value.userType());
if (handler == QProtobufJsonSerializerPrivate::handlers.constEnd()
|| !handler.value().deserializer) {
return false;
}
bool ok = false;
value = handler.value().deserializer(m_state.last().scalarValue, ok);
if (!ok)
setInvalidFormatError();
return true;
}
void QProtobufJsonDeserializerImpl::setUnexpectedEndOfStreamError()
{
setError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
QCoreApplication::translate("QtProtobuf", "JSON: Unexpected end of stream"));
}
void QProtobufJsonDeserializerImpl::setInvalidFormatError()
{
setError(QAbstractProtobufSerializer::Error::InvalidFormat,
QCoreApplication::translate("QtProtobuf",
"JSON: One or more fields have invalid format"));
}
QProtobufJsonSerializerPrivate::SerializerRegistry QProtobufJsonSerializerPrivate::handlers = {};
void QProtobufJsonSerializerPrivate::clearError()
@ -804,19 +755,22 @@ bool QProtobufJsonSerializer::deserializeMessage(QProtobufMessage *message,
QJsonParseError err;
auto document = QJsonDocument::fromJson(data.toByteArray(), &err);
if (err.error != QJsonParseError::NoError) {
d_ptr->setUnexpectedEndOfStreamError();
d_ptr->deserializer.setUnexpectedEndOfStreamError();
return false;
}
if (!document.isObject()) {
d_ptr->setInvalidFormatError();
d_ptr->deserializer.setInvalidFormatError();
return false;
}
d_ptr->activeValue = document.object();
d_ptr->cachedPropertyValue.clear();
d_ptr->cachedIndex = -1;
return d_ptr->deserializeObject(message);
if (auto obj = document.object(); !obj.isEmpty()) {
d_ptr->deserializer.reset(obj);
bool result = d_ptr->deserializer.deserializeMessage(message);
d_ptr->deserializer.reset({});
return result;
}
return true;
}
QT_END_NAMESPACE

View File

@ -287,6 +287,236 @@ QByteArray QProtobufSerializerImpl::encodeHeader(int fieldIndex, QtProtobuf::Wir
return QProtobufSerializerPrivate::serializeVarintCommon<uint32_t>(header);
}
QProtobufDeserializerImpl::QProtobufDeserializerImpl(QProtobufSerializerPrivate *parent)
: m_parent(parent)
{
}
QProtobufDeserializerImpl::~QProtobufDeserializerImpl()
= default;
void QProtobufDeserializerImpl::reset(QByteArrayView data)
{
m_it = QProtobufSelfcheckIterator::fromView(data);
m_state.push_back(data.end());
clearCachedValue();
}
void QProtobufDeserializerImpl::setError(QAbstractProtobufSerializer::Error error,
QAnyStringView errorString)
{
m_parent->lastError = error;
m_parent->lastErrorString = errorString.toString();
}
bool QProtobufDeserializerImpl::deserializeEnum(QVariant &value,
const QProtobufFieldInfo &fieldInfo)
{
const auto fieldFlags = fieldInfo.fieldFlags();
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
QMetaType metaType = value.metaType();
value.convert(QMetaType::fromType<QList<QtProtobuf::int64>>());
bool result = false;
if (m_wireType == QtProtobuf::WireTypes::Varint) {
result = QProtobufSerializerPrivate::deserializeNonPackedList<QtProtobuf::int64>(m_it,
value);
} else if (m_wireType == QtProtobuf::WireTypes::LengthDelimited) {
result = QProtobufSerializerPrivate::deserializeList<QtProtobuf::int64>(m_it, value);
}
value.convert(metaType);
return result;
}
return QProtobufSerializerPrivate::deserializeBasic<QtProtobuf::int64>(m_it, value);
}
int QProtobufDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
{
Q_ASSERT(message);
const auto *ordering = message->propertyOrdering();
Q_ASSERT(ordering != nullptr);
while (m_it.isValid() && m_it != m_state.last()) {
// Each iteration we expect iterator is setup to beginning of next chunk
int fieldNumber = QtProtobuf::InvalidFieldNumber;
const QProtobufSelfcheckIterator fieldBegin = m_it; // copy this, we may need it later
if (!decodeHeader(m_it, fieldNumber, m_wireType)) {
setError(QAbstractProtobufSerializer::Error::InvalidHeader,
"Message received doesn't contain valid header byte.");
return -1;
}
int index = ordering->indexOfFieldNumber(fieldNumber);
if (index == -1) {
// This is an unknown field, it may have been added in a later revision
// of the Message we are currently deserializing. We must store the
// bytes for this field and re-emit them later if this message is
// serialized again.
if (auto length = skipField(fieldBegin); length < 0) {
return -1;
} else if (length > 0 && m_parent->preserveUnknownFields) {
QByteArrayView fieldData(fieldBegin.data(), length);
QProtobufMessagePrivate::storeUnknownEntry(message, fieldData, fieldNumber);
}
continue;
}
if (ordering->fieldFlags(index).testAnyFlags({ QtProtobufPrivate::FieldFlag::Message,
QtProtobufPrivate::FieldFlag::Map })) {
auto
opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
if (!opt) {
setUnexpectedEndOfStreamError();
return -1;
}
quint64 length = *opt;
if (!m_it.isValid() || quint64(m_it.bytesLeft()) < length
|| length > quint64(QByteArray::maxSize())) {
setUnexpectedEndOfStreamError();
return -1;
}
m_state.push_back(m_it.data() + length);
}
return index;
}
if (!m_it.isValid())
setUnexpectedEndOfStreamError();
m_state.pop_back();
return -1;
}
bool QProtobufDeserializerImpl::deserializeScalarField(QVariant &value,
const QtProtobufPrivate::QProtobufFieldInfo
&fieldInfo)
{
QMetaType metaType = value.metaType();
bool isNonPacked = fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::NonPacked);
auto basicHandler = findIntegratedTypeHandler(metaType, isNonPacked);
if (!basicHandler)
return false;
if (basicHandler->wireType != m_wireType) {
// If the handler wiretype mismatches the wiretype received from the
// wire that most probably means that we received the list in wrong
// format. This can happen because of mismatch of the field packed
// option in the protobuf schema on the wire ends. Invert the
// isNonPacked flag and try to find the handler one more time to make
// sure that we cover this exceptional case.
// See the conformance tests
// Required.Proto3.ProtobufInput.ValidDataRepeated.*.UnpackedInput
// for details.
basicHandler = findIntegratedTypeHandler(metaType, !isNonPacked);
if (!basicHandler || basicHandler->wireType != m_wireType) {
setError(QAbstractProtobufSerializer::Error::InvalidHeader,
QCoreApplication::translate("QtProtobuf",
"Invalid wiretype for the %1 "
"field number %1. Expected %2, received %3")
.arg(QString::fromUtf8(metaType.name()))
.arg(fieldInfo.fieldNumber())
.arg(basicHandler ? static_cast<int>(basicHandler->wireType) : -1)
.arg(static_cast<int>(m_wireType)));
value.clear();
return true;
}
}
if (!basicHandler->deserializer(m_it, value)) {
value.clear();
setUnexpectedEndOfStreamError();
}
return true;
}
qsizetype QProtobufDeserializerImpl::skipField(const QProtobufSelfcheckIterator &fieldBegin)
{
switch (m_wireType) {
case QtProtobuf::WireTypes::Varint:
skipVarint();
break;
case QtProtobuf::WireTypes::Fixed32:
m_it += sizeof(decltype(QtProtobuf::fixed32::t));
break;
case QtProtobuf::WireTypes::Fixed64:
m_it += sizeof(decltype(QtProtobuf::fixed64::t));
break;
case QtProtobuf::WireTypes::LengthDelimited:
skipLengthDelimited();
break;
case QtProtobuf::WireTypes::Unknown:
default:
Q_UNREACHABLE();
return 0;
}
if (!m_it.isValid()) {
setUnexpectedEndOfStreamError();
return -1;
}
return std::distance(fieldBegin, m_it);
}
void QProtobufDeserializerImpl::skipVarint()
{
while ((*m_it) & 0x80)
++m_it;
++m_it;
}
void QProtobufDeserializerImpl::skipLengthDelimited()
{
//Get length of length-delimited field
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
if (!opt) {
m_it += m_it.bytesLeft() + 1;
return;
}
QtProtobuf::uint64 length = opt.value();
m_it += length;
}
void QProtobufDeserializerImpl::setUnexpectedEndOfStreamError()
{
setError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
QCoreApplication::translate("QtProtobuf", "Unexpected end of stream"));
}
bool QProtobufDeserializerImpl::decodeHeader(QProtobufSelfcheckIterator &it, int &fieldIndex,
QtProtobuf::WireTypes &wireType)
{
if (it.bytesLeft() == 0)
return false;
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<uint32_t>(it);
if (!opt)
return false;
uint32_t header = opt.value();
wireType = static_cast<QtProtobuf::WireTypes>(header & 0b00000111);
fieldIndex = header >> 3;
constexpr int maxFieldIndex = (1 << 29) - 1;
return fieldIndex <= maxFieldIndex && fieldIndex > 0
&& (wireType == QtProtobuf::WireTypes::Varint || wireType == QtProtobuf::WireTypes::Fixed64
|| wireType == QtProtobuf::WireTypes::Fixed32
|| wireType == QtProtobuf::WireTypes::LengthDelimited);
}
QProtobufSerializerPrivate::QProtobufSerializerPrivate() : deserializer(this)
{
}
void QProtobufSerializerPrivate::clearError()
{
lastError = QAbstractProtobufSerializer::Error::None;
lastErrorString.clear();
}
/*!
Constructs a new serializer instance.
*/
@ -310,327 +540,13 @@ QByteArray QProtobufSerializer::serializeMessage(const QProtobufMessage *message
return result;
}
void QProtobufSerializerPrivate::setUnexpectedEndOfStreamError()
{
setDeserializationError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
QCoreApplication::translate("QtProtobuf", "Unexpected end of stream"));
}
void QProtobufSerializerPrivate::clearError()
{
lastError = QAbstractProtobufSerializer::Error::None;
lastErrorString.clear();
}
bool QProtobufSerializer::deserializeMessage(QProtobufMessage *message, QByteArrayView data) const
{
d_ptr->clearCachedValue();
d_ptr->clearError();
d_ptr->it = QProtobufSelfcheckIterator::fromView(data);
bool ok = true;
while (d_ptr->it.isValid() && d_ptr->it != data.end()) {
if (!d_ptr->deserializeProperty(message)) {
ok = false;
break;
}
}
if (!d_ptr->storeCachedValue(message) || !ok)
return false;
if (!d_ptr->it.isValid())
d_ptr->setUnexpectedEndOfStreamError();
return d_ptr->it.isValid();
}
bool QProtobufSerializerPrivate::deserializeObject(QProtobufMessage *message)
{
if (it.bytesLeft() == 0) {
setUnexpectedEndOfStreamError();
return false;
}
std::optional<QByteArray>
array = QProtobufSerializerPrivate::deserializeLengthDelimited(it);
if (!array) {
setUnexpectedEndOfStreamError();
return false;
}
auto prevCachedRepeatedIterator = std::move(cachedRepeatedIterator);
auto restoreOnReturn = qScopeGuard([prevIt = it, prevCachedPropertyValue = cachedPropertyValue,
prevCachedIndex = cachedIndex, &prevCachedRepeatedIterator,
this]() {
it = prevIt;
cachedPropertyValue = prevCachedPropertyValue;
cachedIndex = prevCachedIndex;
cachedRepeatedIterator = std::move(prevCachedRepeatedIterator);
});
clearCachedValue();
QByteArrayView data = *array;
clearError();
it = QProtobufSelfcheckIterator::fromView(data);
bool ok = true;
while (it.isValid() && it != data.end()) {
if (!deserializeProperty(message)) {
ok = false;
break;
}
}
if (!storeCachedValue(message) || !ok)
return false;
if (!it.isValid())
setUnexpectedEndOfStreamError();
return it.isValid();
}
bool QProtobufSerializerPrivate::deserializeEnumList(QList<QtProtobuf::int64> &value)
{
QVariant variantValue;
if (!QProtobufSerializerPrivate::deserializeList<QtProtobuf::int64>(it, variantValue)) {
setUnexpectedEndOfStreamError();
return false;
}
value = variantValue.value<QList<QtProtobuf::int64>>();
return true;
}
/*!
\internal
Decode a property field index and its serialization type from input bytes
Iterator: that points to header with encoded field index and serialization type
fieldIndex: Decoded index of a property in parent object
wireType: Decoded serialization type used for the property with index
Return true if both decoded wireType and fieldIndex have "allowed" values and false, otherwise
*/
bool QProtobufSerializerPrivate::decodeHeader(QProtobufSelfcheckIterator &it,
int &fieldIndex,
QtProtobuf::WireTypes &wireType)
{
if (it.bytesLeft() == 0)
return false;
auto opt = deserializeVarintCommon<uint32_t>(it);
if (!opt)
return false;
uint32_t header = opt.value();
wireType = static_cast<QtProtobuf::WireTypes>(header & 0b00000111);
fieldIndex = header >> 3;
constexpr int maxFieldIndex = (1 << 29) - 1;
return fieldIndex <= maxFieldIndex && fieldIndex > 0
&& (wireType == QtProtobuf::WireTypes::Varint
|| wireType == QtProtobuf::WireTypes::Fixed64
|| wireType == QtProtobuf::WireTypes::Fixed32
|| wireType == QtProtobuf::WireTypes::LengthDelimited);
}
void QProtobufSerializerPrivate::skipVarint(QProtobufSelfcheckIterator &it)
{
while ((*it) & 0x80)
++it;
++it;
}
void QProtobufSerializerPrivate::skipLengthDelimited(QProtobufSelfcheckIterator &it)
{
//Get length of length-delimited field
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(it);
if (!opt) {
it += it.bytesLeft() + 1;
return;
}
QtProtobuf::uint64 length = opt.value();
it += length;
}
qsizetype QProtobufSerializerPrivate::skipSerializedFieldBytes(QProtobufSelfcheckIterator &it, QtProtobuf::WireTypes type)
{
const auto *initialIt = QByteArray::const_iterator(it);
switch (type) {
case QtProtobuf::WireTypes::Varint:
skipVarint(it);
break;
case QtProtobuf::WireTypes::Fixed32:
it += sizeof(decltype(QtProtobuf::fixed32::t));
break;
case QtProtobuf::WireTypes::Fixed64:
it += sizeof(decltype(QtProtobuf::fixed64::t));
break;
case QtProtobuf::WireTypes::LengthDelimited:
skipLengthDelimited(it);
break;
case QtProtobuf::WireTypes::Unknown:
default:
Q_UNREACHABLE();
return 0;
}
return std::distance(initialIt, QByteArray::const_iterator(it));
}
bool QProtobufSerializerPrivate::deserializeProperty(QProtobufMessage *message)
{
Q_ASSERT(message != nullptr);
Q_ASSERT(it.isValid() && it.bytesLeft() > 0);
//Each iteration we expect iterator is setup to beginning of next chunk
int fieldNumber = QtProtobuf::InvalidFieldNumber;
QtProtobuf::WireTypes wireType = QtProtobuf::WireTypes::Unknown;
const QProtobufSelfcheckIterator itBeforeHeader = it; // copy this, we may need it later
if (!QProtobufSerializerPrivate::decodeHeader(it, fieldNumber, wireType)) {
setDeserializationError(
QAbstractProtobufSerializer::Error::InvalidHeader,
QCoreApplication::translate("QtProtobuf",
"Message received doesn't contain valid header byte."));
return false;
}
auto ordering = message->propertyOrdering();
Q_ASSERT(ordering != nullptr);
int index = ordering->indexOfFieldNumber(fieldNumber);
if (index == -1) {
// This is an unknown field, it may have been added in a later revision
// of the Message we are currently deserializing. We must store the
// bytes for this field and re-emit them later if this message is
// serialized again.
qsizetype length = std::distance(itBeforeHeader, it); // size of header
length += QProtobufSerializerPrivate::skipSerializedFieldBytes(it, wireType);
if (!it.isValid()) {
setUnexpectedEndOfStreamError();
return false;
}
if (preserveUnknownFields) {
QProtobufMessagePrivate::storeUnknownEntry(message,
QByteArrayView(itBeforeHeader.data(),
length),
fieldNumber);
}
return true;
}
QProtobufFieldInfo fieldInfo(*ordering, index);
if (cachedIndex != index) {
if (!storeCachedValue(message))
return false;
cachedPropertyValue = QtProtobufSerializerHelpers::messageProperty(message, fieldInfo,
true);
cachedIndex = index;
}
QMetaType metaType = cachedPropertyValue.metaType();
qProtoDebug() << "wireType:" << wireType << "metaType:" << metaType.name()
<< "currentByte:" << QString::number((*it), 16);
if (metaType.flags() & QMetaType::IsPointer) {
auto *messageProperty = cachedPropertyValue.value<QProtobufMessage *>();
Q_ASSERT(messageProperty != nullptr);
return deserializeObject(messageProperty);
}
const auto fieldFlags = fieldInfo.fieldFlags();
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Enum)) {
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
auto intList = cachedPropertyValue.value<QList<QtProtobuf::int64>>();
if (deserializeEnumList(intList)) {
cachedPropertyValue.setValue(intList);
return true;
}
return false;
} else {
if (deserializeBasic<QtProtobuf::int64>(it, cachedPropertyValue))
return true;
return false;
}
}
bool isNonPacked = fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::NonPacked);
auto basicHandler = findIntegratedTypeHandler(metaType, isNonPacked);
if (basicHandler) {
if (basicHandler->wireType != wireType) {
// If the handler wiretype mismatches the wiretype received from the
// wire that most probably means that we received the list in wrong
// format. This can happen because of mismatch of the field packed
// option in the protobuf schema on the wire ends. Invert the
// isNonPacked flag and try to find the handler one more time to make
// sure that we cover this exceptional case.
// See the conformance tests
// Required.Proto3.ProtobufInput.ValidDataRepeated.*.UnpackedInput
// for details.
basicHandler = findIntegratedTypeHandler(metaType, !isNonPacked);
if (!basicHandler || basicHandler->wireType != wireType) {
setDeserializationError(
QAbstractProtobufSerializer::Error::InvalidHeader,
QCoreApplication::translate("QtProtobuf",
"Message received has invalid wiretype for the "
"field number %1. Expected %2, received %3")
.arg(fieldNumber)
.arg(basicHandler ? static_cast<int>(basicHandler->wireType) : -1)
.arg(static_cast<int>(wireType)));
return false;
}
}
if (!basicHandler->deserializer(it, cachedPropertyValue)) {
setUnexpectedEndOfStreamError();
return false;
}
return true;
}
if (cachedPropertyValue.canView(QMetaType::fromType<QProtobufRepeatedIterator>())) {
if (!cachedRepeatedIterator.isValid()) {
cachedRepeatedIterator = cachedPropertyValue.view<QProtobufRepeatedIterator>();
}
if (deserializeObject(cachedRepeatedIterator.addNext())) {
cachedRepeatedIterator.push();
return true;
}
return false;
}
auto handler = QtProtobufPrivate::findHandler(metaType);
if (!handler.deserializer) {
qProtoWarning() << "No deserializer for type" << metaType.name();
QString error = QString::fromUtf8("No deserializer is registered for type %1")
.arg(QString::fromUtf8(metaType.name()));
setDeserializationError(QAbstractProtobufSerializer::Error::UnknownType,
QCoreApplication::translate("QtProtobuf", error.toUtf8().data()));
return false;
}
handler.deserializer([this](QProtobufMessage
*message) { return this->deserializeObject(message); },
cachedPropertyValue.data());
return true;
}
bool QProtobufSerializerPrivate::storeCachedValue(QProtobufMessage *message)
{
bool ok = true;
if (cachedIndex >= 0 && !cachedPropertyValue.isNull()) {
const auto *ordering = message->propertyOrdering();
QProtobufFieldInfo fieldInfo(*ordering, cachedIndex);
ok = QtProtobufSerializerHelpers::setMessageProperty(message, fieldInfo,
cachedPropertyValue);
clearCachedValue();
}
return ok;
}
void QProtobufSerializerPrivate::clearCachedValue()
{
cachedPropertyValue.clear();
cachedIndex = -1;
cachedRepeatedIterator = QProtobufRepeatedIterator();
d_ptr->deserializer.reset(data);
d_ptr->deserializer.deserializeMessage(message);
d_ptr->deserializer.reset({});
return d_ptr->lastError == QAbstractProtobufSerializer::Error::None;
}
/*!
@ -651,13 +567,6 @@ QString QProtobufSerializer::lastErrorString() const
return d_ptr->lastErrorString;
}
void QProtobufSerializerPrivate::setDeserializationError(
QAbstractProtobufSerializer::Error error, const QString &errorString)
{
lastError = error;
lastErrorString = errorString;
}
/*!
Controls whether the unknown fields received from the wire should be
stored in the resulting message or if it should be omitted, based

View File

@ -23,9 +23,10 @@
#include <QtProtobuf/qtprotobuftypes.h>
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
#include <QtProtobuf/private/qtprotobuflogging_p.h>
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
#include <QtProtobuf/private/qprotobufselfcheckiterator_p.h>
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
#include <QtProtobuf/private/qtprotobuflogging_p.h>
#include <QtCore/qendian.h>
#include <QtCore/qstring.h>
@ -68,6 +69,40 @@ private:
Q_DISABLE_COPY_MOVE(QProtobufSerializerImpl)
};
class QProtobufDeserializerImpl final : public QProtobufDeserializerBase
{
public:
explicit QProtobufDeserializerImpl(QProtobufSerializerPrivate *parent);
~QProtobufDeserializerImpl();
void reset(QByteArrayView data);
QProtobufSelfcheckIterator m_it;
private:
void setError(QAbstractProtobufSerializer::Error error, QAnyStringView errorString) override;
bool deserializeEnum(QVariant &value,
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) override;
int nextFieldIndex(QProtobufMessage *message) override;
bool deserializeScalarField(QVariant &value,
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) override;
qsizetype skipField(const QProtobufSelfcheckIterator &fieldBegin);
void skipVarint();
void skipLengthDelimited();
void setUnexpectedEndOfStreamError();
[[nodiscard]]
static bool decodeHeader(QProtobufSelfcheckIterator &it, int &fieldIndex,
QtProtobuf::WireTypes &wireType);
QtProtobuf::WireTypes m_wireType = QtProtobuf::WireTypes::Unknown;
QList<QByteArrayView::iterator> m_state;
QProtobufSerializerPrivate *m_parent = nullptr;
Q_DISABLE_COPY_MOVE(QProtobufDeserializerImpl)
};
class QProtobufSerializerPrivate
{
// The below type trait structures help to determine the required encoding method for protobuf
@ -175,7 +210,7 @@ public:
QtProtobuf::WireTypes wireType; // Serialization WireType
};
QProtobufSerializerPrivate() = default;
QProtobufSerializerPrivate();
~QProtobufSerializerPrivate() = default;
// ###########################################################################
// Serializers
@ -487,10 +522,6 @@ public:
return { result };
}
[[nodiscard]]
static bool decodeHeader(QProtobufSelfcheckIterator &it, int &fieldIndex,
QtProtobuf::WireTypes &wireType);
/*!
Gets length of a byte-array and prepends to it its serialized length value
using the appropriate serialization algorithm
@ -515,40 +546,16 @@ public:
return s(variantValue.value<T>(), header);
}
// this set of 3 methods is used to skip bytes corresponding to an unexpected property
// in a serialized message met while the message being deserialized
static qsizetype skipSerializedFieldBytes(QProtobufSelfcheckIterator &it,
QtProtobuf::WireTypes type);
static void skipVarint(QProtobufSelfcheckIterator &it);
static void skipLengthDelimited(QProtobufSelfcheckIterator &it);
bool deserializeObject(QProtobufMessage *message);
bool deserializeEnumList(QList<QtProtobuf::int64> &value);
[[nodiscard]] bool deserializeProperty(QProtobufMessage *message);
void setDeserializationError(QAbstractProtobufSerializer::Error error,
const QString &errorString);
void clearError();
void setUnexpectedEndOfStreamError();
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
void clearCachedValue();
QAbstractProtobufSerializer::Error lastError =
QAbstractProtobufSerializer::Error::UnknownType;
QString lastErrorString;
QProtobufSelfcheckIterator it;
bool preserveUnknownFields = true;
QVariant cachedPropertyValue;
QProtobufRepeatedIterator cachedRepeatedIterator;
int cachedIndex = -1;
QProtobufSerializerImpl serializer;
QProtobufDeserializerImpl deserializer;
private:
Q_DISABLE_COPY_MOVE(QProtobufSerializerPrivate)

View File

@ -1,4 +1,3 @@
Required.Proto3.ProtobufInput.ValidDataRepeated.ENUM.UnpackedInput.ProtobufOutput
Required.Proto3.JsonInput.Any.JsonOutput
Required.Proto3.JsonInput.Any.ProtobufOutput
Required.Proto3.JsonInput.AnyNested.JsonOutput
@ -105,7 +104,6 @@ Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput
Required.Proto3.JsonInput.ValueAcceptObject.ProtobufOutput
Required.Proto3.JsonInput.ValueAcceptString.JsonOutput
Required.Proto3.JsonInput.ValueAcceptString.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataRepeated.ENUM.UnpackedInput.JsonOutput
Required.DurationProtoInputTooLarge.JsonOutput
Required.DurationProtoInputTooSmall.JsonOutput
Required.TimestampProtoInputTooLarge.JsonOutput

View File

@ -260,14 +260,14 @@ void QtProtobufRepeatedTypesJsonDeserializationTest::invalidTypeTest()
// expected sint, string is used
RepeatedSInt64Message test;
test.deserialize(serializer.get(), "{\"testRepeatedInt\":[1,321,\"abcd\",12324523123123,-3,3]}"_ba);
QVERIFY(test.testRepeatedInt().size() == 0);
QCOMPARE(test.testRepeatedInt().size(), 2);
QCOMPARE(serializer->lastError(), QAbstractProtobufSerializer::Error::InvalidFormat);
// expected bool, float is used
RepeatedBoolMessage boolMsg;
boolMsg.deserialize(serializer.get(),
"{\"testRepeatedBool\":[true,true,true,7.8,false,false,false,false,false,false,false,false,true]}"_ba);
QVERIFY(test.testRepeatedInt().size() == 0);
QCOMPARE(boolMsg.testRepeatedBool().size(), 3);
QCOMPARE(serializer->lastError(), QAbstractProtobufSerializer::Error::InvalidFormat);
}