Revision control
++ // Copyright (C) 2023 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
++ // Qt-Security score:critical reason:data-parser
++
++ #include <QtProtobuf/qprotobufjsonserializer.h>
++
++ #include <QtProtobuf/private/qtprotobuf-config_p.h>
++ #include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
++ #include <QtProtobuf/private/protobufscalarjsonserializers_p.h>
++ #include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
++ #include <QtProtobuf/private/qprotobufjsonserializer_p.h>
++ #include <QtProtobuf/private/qprotobufregistration_p.h>
++ #include <QtProtobuf/private/qprotobufserializerbase_p.h>
++ #include <QtProtobuf/private/qtprotobufdefs_p.h>
++ #include <QtProtobuf/private/qtprotobufserializerhelpers_p.h>
++
++ #include <QtCore/qcoreapplication.h>
++ #include <QtCore/qdatetime.h>
++ #include <QtCore/qhash.h>
++ #include <QtCore/qjsonarray.h>
++ #include <QtCore/qjsondocument.h>
++ #include <QtCore/qjsonobject.h>
++ #if !QT_CONFIG(protobuf_unsafe_registry)
++ # include <QtCore/qreadwritelock.h>
++ #endif
++ #include <QtCore/qtimezone.h>
++ #include <QtCore/qvariant.h>
++
++ #include <cmath>
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \class QProtobufJsonSerializer
++ \inmodule QtProtobuf
++ \since 6.7
++ \brief The QProtobufJsonSerializer class is an interface that represents
++ basic functions for serialization/deserialization of QProtobufMessage
++ objects to JSON.
++
++ The QProtobufJsonSerializer class registers serializers/deserializers for
++ classes implementing a protobuf message, inheriting \l QProtobufMessage. These
++ classes are generated automatically, based on a \c{.proto} file, using the CMake
++ function \l qt_add_protobuf or by running
++ \l {The qtprotobufgen Tool} {qtprotobufgen} directly.
++ */
++
++ using namespace Qt::StringLiterals;
++ using namespace QtProtobufPrivate;
++ using namespace ProtobufScalarJsonSerializers;
++
++ namespace {
++
++ struct JsonHandlerRegistry
++ {
++ void registerHandler(QMetaType metaType, QtProtobufPrivate::CustomJsonSerializer serializer,
++ QtProtobufPrivate::CustomJsonDeserializer deserializer)
++ {
++ #if !QT_CONFIG(protobuf_unsafe_registry)
++ QWriteLocker locker(&m_lock);
++ #endif
++ m_registry[metaType] = { serializer, deserializer };
++ }
++
++ QtProtobufPrivate::CustomJsonSerializer findSerializer(QMetaType metaType)
++ {
++ #if !QT_CONFIG(protobuf_unsafe_registry)
++ QReadLocker locker(&m_lock);
++ #endif
++ const auto it = m_registry.constFind(metaType);
++ if (it != m_registry.constEnd())
++ return it.value().first;
++ return nullptr;
++ }
++
++ QtProtobufPrivate::CustomJsonDeserializer findDeserializer(QMetaType metaType)
++ {
++ #if !QT_CONFIG(protobuf_unsafe_registry)
++ QReadLocker locker(&m_lock);
++ #endif
++ const auto it = m_registry.constFind(metaType);
++ if (it != m_registry.constEnd())
++ return it.value().second;
++ return nullptr;
++ }
++
++ private:
++ using Handler = std::pair<QtProtobufPrivate::CustomJsonSerializer,
++ QtProtobufPrivate::CustomJsonDeserializer>;
++ #if !QT_CONFIG(protobuf_unsafe_registry)
++ QReadWriteLock m_lock;
++ #endif
++ QHash<QMetaType, Handler> m_registry;
++ };
++ Q_GLOBAL_STATIC(JsonHandlerRegistry, jsonHandlersRegistry)
++
++ inline QString convertJsonKeyToJsonName(QStringView name)
++ {
++ QString result;
++ result.reserve(name.size());
++ bool nextUpperCase = false;
++ for (const auto &c : name) {
++ if (c == QChar::fromLatin1('_')) {
++ nextUpperCase = true;
++ continue;
++ }
++ result.append(nextUpperCase ? c.toUpper() : c);
++ nextUpperCase = false;
++ }
++ return result;
++ }
++
++ }
++
++ void QtProtobufPrivate::registerCustomJsonHandler(QMetaType metaType,
++ QtProtobufPrivate::CustomJsonSerializer
++ serializer,
++ QtProtobufPrivate::CustomJsonDeserializer
++ deserializer)
++ {
++ jsonHandlersRegistry->registerHandler(metaType, serializer, deserializer);
++ }
++
++ QtProtobufPrivate::CustomJsonSerializer
++ QtProtobufPrivate::findCustomJsonSerializer(QMetaType metaType)
++ {
++ return jsonHandlersRegistry->findSerializer(metaType);
++ }
++
++ QtProtobufPrivate::CustomJsonDeserializer
++ QtProtobufPrivate::findCustomJsonDeserializer(QMetaType metaType)
++ {
++ return jsonHandlersRegistry->findDeserializer(metaType);
++ }
++
++ class QProtobufJsonSerializerImpl final : public QProtobufSerializerBase
++ {
++ public:
++ QProtobufJsonSerializerImpl() = default;
++ ~QProtobufJsonSerializerImpl();
++
++ const QJsonObject &result() const { return m_result; }
++
++ void reset();
++
++ protected:
++ void serializeMessageField(const QProtobufMessage *message,
++ const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) override;
++
++ private:
++ bool serializeEnum(QVariant &value, const QProtobufFieldInfo &fieldInfo) override;
++ bool serializeScalarField(const QVariant &value, const QProtobufFieldInfo &fieldInfo) override;
++ void serializeMessageFieldBegin() override;
++ void serializeMessageFieldEnd(const QProtobufMessage *message,
++ const QProtobufFieldInfo &fieldInfo) override;
++
++ QJsonObject m_result;
++ QList<QJsonObject> m_state;
++
++ 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;
++ bool hasError() const override;
++ void setUnexpectedEndOfStreamError();
++ void setInvalidFormatError();
++
++ protected:
++ bool deserializeMessageField(QProtobufMessage *message) override;
++
++ 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 = {QJsonValue::Undefined};
++ };
++
++ QList<JsonDeserializerState> m_state;
++ QProtobufJsonSerializerPrivate *m_parent = nullptr;
++ };
++
++ class QProtobufJsonSerializerPrivate final
++ {
++ Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
++ public:
++ using Serializer = std::function<QJsonValue(const QVariant &)>;
++ using Deserializer = std::function<QVariant(const QJsonValue&, bool &ok)>;
++
++ struct SerializationHandlers {
++ // serializer assigned to class
++ Serializer serializer;
++ // deserializer assigned to class
++ Deserializer deserializer;
++ ProtobufFieldPresenceChecker::Function isPresent;
++ };
++
++ template <typename T>
++ static SerializationHandlers createCommonHandler()
++ {
++ return { serializeCommon<T>, deserializeCommon<T>,
++ ProtobufFieldPresenceChecker::isPresent<T> };
++ }
++
++ template <typename L, typename T>
++ static SerializationHandlers createCommonListHandler()
++ {
++ return { serializeList<L>, deserializeList<L, T>,
++ ProtobufFieldPresenceChecker::isPresent<L> };
++ }
++
++ template<typename T, if_json_floating_point<T> = true>
++ static bool isPresent(const QVariant &value)
++ {
++ const T val = value.value<T>();
++ return val != 0 || std::signbit(val);
++ }
++
++ using SerializerRegistry = QHash<int/*metatypeid*/, SerializationHandlers>;
++
++ QProtobufJsonSerializerPrivate();
++ ~QProtobufJsonSerializerPrivate() = default;
++
++ void clearError();
++
++ QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
++ QString lastErrorString;
++
++ static SerializerRegistry handlers;
++
++ QProtobufJsonSerializerImpl serializer;
++ QProtobufJsonDeserializerImpl deserializer;
++ };
++
++ QProtobufJsonSerializerImpl::~QProtobufJsonSerializerImpl() = default;
++
++ void QProtobufJsonSerializerImpl::reset()
++ {
++ m_result = {};
++ m_state.clear();
++ }
++
++ void QProtobufJsonSerializerImpl::serializeMessageField(const QProtobufMessage *message,
++ const QtProtobufPrivate::QProtobufFieldInfo
++ &fieldInfo)
++ {
++ if (!message)
++ return;
++
++ const auto *metaObject = QtProtobufSerializerHelpers::messageMetaObject(message);
++
++ if (auto *serializer = QtProtobufPrivate::findCustomJsonSerializer(metaObject->metaType())) {
++ if (const QJsonValue value = serializer(message); !value.isUndefined()) {
++ if (fieldInfo.fieldFlags().testAnyFlags(QtProtobufPrivate::FieldFlag::Repeated)) {
++ auto array = m_result.value(fieldInfo.jsonName().toString()).toArray();
++ array.append(value);
++ m_result.insert(fieldInfo.jsonName().toString(), array);
++ } else {
++ m_result.insert(fieldInfo.jsonName().toString(), value);
++ }
++ }
++ } else {
++ QProtobufSerializerBase::serializeMessageField(message, fieldInfo);
++ }
++ }
++
++ bool QProtobufJsonSerializerImpl::serializeEnum(QVariant &value,
++ const QProtobufFieldInfo &fieldInfo)
++
++ {
++ const auto jsonName = fieldInfo.jsonName();
++ if (fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
++ if (!value.convert(QMetaType::fromType<QStringList>()))
++ return false;
++ if (!ProtobufFieldPresenceChecker::isPresent<QStringList>(value))
++ return true;
++ m_result.insert(jsonName.toString(), serializeList<QStringList>(value));
++ } else {
++ if (!value.convert(QMetaType::fromType<QString>()))
++ return false;
++ if (!ProtobufFieldPresenceChecker::isPresent<QString>(value)
++ && !mustSerializeDefaultValue(fieldInfo.fieldFlags())) {
++ return true;
++ }
++ m_result.insert(jsonName.toString(), serializeCommon<QString>(value));
++ }
++ return true;
++ }
++
++ bool QProtobufJsonSerializerImpl::serializeScalarField(const QVariant &value,
++ const QProtobufFieldInfo &fieldInfo)
++ {
++ const auto it = QProtobufJsonSerializerPrivate::handlers.constFind(value.userType());
++ // Is not a protobuf scalar value type
++ if (it == QProtobufJsonSerializerPrivate::handlers.cend())
++ return false;
++
++ // Field is empty
++ if (!it->isPresent(value) && !mustSerializeDefaultValue(fieldInfo.fieldFlags()))
++ return true;
++
++ // If serializer is not defined we should use the standard QJsonValue cast from variant
++ m_result.insert(fieldInfo.jsonName().toString(),
++ it->serializer ? it->serializer(value) : QJsonValue::fromVariant(value));
++ return true;
++ }
++
++ void QProtobufJsonSerializerImpl::serializeMessageFieldBegin()
++ {
++ m_state.emplaceBack(std::move(m_result));
++ m_result = {};
++ }
++
++ void QProtobufJsonSerializerImpl::serializeMessageFieldEnd(const QProtobufMessage *message,
++ const QProtobufFieldInfo &field)
++ {
++ QJsonObject store = m_state.takeLast();
++ QString fieldName = field.jsonName().toString();
++ if (field.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
++ // Repeated fields are stored in array property
++ // "repeatedField": [{...}, {...}, ...]
++ QJsonArray array = store.value(fieldName).toArray();
++ array.append(m_result);
++ store.insert(fieldName, array);
++ } else if (field.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Map)) {
++ // Maps are stored as fields in inner JSON object
++ // "mapField": { "key1": "value1", ... }
++ QJsonObject mapObject = store.value(fieldName).toObject();
++ // We don't need to serialize key in general, since all JSON object fields should be
++ // represented as strings, and types that can be map keys have valid string converters.
++ mapObject.insert(message->property("key").toString(), m_result.value("value"_L1));
++ store.insert(fieldName, mapObject);
++ } else {
++ store.insert(fieldName, m_result);
++ }
++ m_result = store;
++ }
++
++ QProtobufJsonDeserializerImpl::QProtobufJsonDeserializerImpl(QProtobufJsonSerializerPrivate *parent)
++ : QProtobufDeserializerBase(false), m_parent(parent)
++ {
++ }
++
++ QProtobufJsonDeserializerImpl::~QProtobufJsonDeserializerImpl()
++ = default;
++
++ void QProtobufJsonDeserializerImpl::reset(QJsonObject obj)
++ {
++ clearCachedValue();
++ 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::hasError() const
++ {
++ return m_parent->lastError != QAbstractProtobufSerializer::Error::None;
++ }
++
++ bool QProtobufJsonDeserializerImpl::deserializeMessageField(QProtobufMessage *message)
++ {
++ if (!message)
++ return true;
++
++ const auto &value = m_state.last().scalarValue;
++ if (!value.isUndefined()) {
++ const auto *metaObject = QtProtobufSerializerHelpers::messageMetaObject(message);
++ if (auto *deserializer = QtProtobufPrivate::findCustomJsonDeserializer(metaObject
++ ->metaType())) {
++ return deserializer(message, value);
++ }
++ setInvalidFormatError();
++ return false;
++ }
++ return QProtobufDeserializerBase::deserializeMessageField(message);
++ }
++
++ 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 = 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 = deserializeCommon<QString>(state.scalarValue, ok);
++ } else {
++ value = 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;
++ state.scalarValue = {};
++ continue;
++ }
++
++ auto nextValue = array.takeAt(0);
++ if (nextValue.isNull()) {
++ setInvalidFormatError();
++ return -1;
++ }
++
++ state.obj.insert(*it, array);
++ if (nextValue.isObject()) {
++ m_state.push_back({ nextValue.toObject() });
++ } else {
++ state.scalarValue = nextValue;
++ }
++ } 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.isArray() || val.isUndefined()) {
++ setInvalidFormatError();
++ return -1;
++ }
++
++ if (val.isObject()) {
++ ++state.index;
++ m_state.push_back({ val.toObject() });
++ } else {
++ state.scalarValue = val;
++ ++state.index;
++ }
++ } 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 = {};
++
++ QProtobufJsonSerializerPrivate::QProtobufJsonSerializerPrivate() : deserializer(this)
++ {
++ [[maybe_unused]] static bool initialized = []() -> bool {
++ handlers[qMetaTypeId<QtProtobuf::int32>()] = createCommonHandler<QtProtobuf::int32>();
++ handlers[qMetaTypeId<QtProtobuf::sfixed32>()] = createCommonHandler<QtProtobuf::sfixed32>();
++ handlers[qMetaTypeId<QtProtobuf::sint32>()] = createCommonHandler<QtProtobuf::sint32>();
++ handlers[qMetaTypeId<QtProtobuf::uint32>()] = createCommonHandler<QtProtobuf::uint32>();
++ handlers[qMetaTypeId<QtProtobuf::fixed32>()] = createCommonHandler<QtProtobuf::fixed32>();
++ handlers[qMetaTypeId<QtProtobuf::sint64>()] = createCommonHandler<QtProtobuf::sint64>();
++ handlers[qMetaTypeId<QtProtobuf::int64>()] = createCommonHandler<QtProtobuf::int64>();
++ handlers[qMetaTypeId<QtProtobuf::sfixed64>()] = createCommonHandler<QtProtobuf::sfixed64>();
++ handlers[qMetaTypeId<QtProtobuf::uint64>()] = createCommonHandler<QtProtobuf::uint64>();
++ handlers[qMetaTypeId<QtProtobuf::fixed64>()] = createCommonHandler<QtProtobuf::fixed64>();
++ handlers[qMetaTypeId<bool>()] = createCommonHandler<bool>();
++ handlers[QMetaType::QString] = createCommonHandler<QString>();
++ handlers[QMetaType::QByteArray] = createCommonHandler<QByteArray>();
++ handlers[QMetaType::Float] = { serializeCommon<float>, deserializeCommon<float>,
++ QProtobufJsonSerializerPrivate::isPresent<float> };
++ handlers[QMetaType::Double] = { serializeCommon<double>, deserializeCommon<double>,
++ isPresent<double> };
++
++ handlers[qMetaTypeId<QtProtobuf::boolList>()] = createCommonListHandler<
++ QtProtobuf::boolList, bool>();
++ handlers[qMetaTypeId<QtProtobuf::int32List>()] = createCommonListHandler<
++ QtProtobuf::int32List, QtProtobuf::int32>();
++ handlers[qMetaTypeId<QtProtobuf::int64List>()] = createCommonListHandler<
++ QtProtobuf::int64List, QtProtobuf::int64>();
++ handlers[qMetaTypeId<QtProtobuf::sint32List>()] = createCommonListHandler<
++ QtProtobuf::sint32List, QtProtobuf::sint32>();
++ handlers[qMetaTypeId<QtProtobuf::sint64List>()] = createCommonListHandler<
++ QtProtobuf::sint64List, QtProtobuf::sint64>();
++ handlers[qMetaTypeId<QtProtobuf::uint32List>()] = createCommonListHandler<
++ QtProtobuf::uint32List, QtProtobuf::uint32>();
++ handlers[qMetaTypeId<QtProtobuf::uint64List>()] = createCommonListHandler<
++ QtProtobuf::uint64List, QtProtobuf::uint64>();
++ handlers[qMetaTypeId<QtProtobuf::fixed32List>()] = createCommonListHandler<
++ QtProtobuf::fixed32List, QtProtobuf::fixed32>();
++ handlers[qMetaTypeId<QtProtobuf::fixed64List>()] = createCommonListHandler<
++ QtProtobuf::fixed64List, QtProtobuf::fixed64>();
++ handlers[qMetaTypeId<QtProtobuf::sfixed32List>()] = createCommonListHandler<
++ QtProtobuf::sfixed32List, QtProtobuf::sfixed32>();
++ handlers[qMetaTypeId<QtProtobuf::sfixed64List>()] = createCommonListHandler<
++ QtProtobuf::sfixed64List, QtProtobuf::sfixed64>();
++ handlers[qMetaTypeId<QtProtobuf::floatList>()] = createCommonListHandler<
++ QtProtobuf::floatList, float>();
++ handlers[qMetaTypeId<QtProtobuf::doubleList>()] = createCommonListHandler<
++ QtProtobuf::doubleList, double>();
++ handlers[qMetaTypeId<QStringList>()] = createCommonListHandler<QStringList, QString>();
++ handlers[qMetaTypeId<QByteArrayList>()] = createCommonListHandler<QByteArrayList,
++ QByteArray>();
++ return true;
++ }();
++ }
++
++ void QProtobufJsonSerializerPrivate::clearError()
++ {
++ lastError = QAbstractProtobufSerializer::Error::None;
++ lastErrorString.clear();
++ }
++
++ QProtobufJsonSerializer::QProtobufJsonSerializer() : d_ptr(new QProtobufJsonSerializerPrivate)
++ {
++ }
++
++ QProtobufJsonSerializer::~QProtobufJsonSerializer() = default;
++
++ /*!
++ Returns the last deserialization error for the serializer instance.
++ \sa lastErrorString()
++ */
++ QAbstractProtobufSerializer::Error QProtobufJsonSerializer::lastError() const
++ {
++ return d_ptr->lastError;
++ }
++
++ /*!
++ Returns the last deserialization error string for the serializer instance.
++ \sa lastError()
++ */
++ QString QProtobufJsonSerializer::lastErrorString() const
++ {
++ return d_ptr->lastErrorString;
++ }
++
++ QByteArray QProtobufJsonSerializer::serializeMessage(const QProtobufMessage *message) const
++ {
++ d_ptr->clearError();
++ d_ptr->serializer.reset();
++ d_ptr->serializer.serializeMessage(message);
++ auto result = QJsonDocument(d_ptr->serializer.result()).toJson(QJsonDocument::Compact);
++ d_ptr->serializer.reset();
++ return result;
++ }
++
++ bool QProtobufJsonSerializer::deserializeMessage(QProtobufMessage *message,
++ QByteArrayView data) const
++ {
++ d_ptr->clearError();
++ QJsonParseError err;
++ auto document = QJsonDocument::fromJson(data.toByteArray(), &err);
++ if (err.error != QJsonParseError::NoError) {
++ d_ptr->deserializer.setUnexpectedEndOfStreamError();
++ return false;
++ }
++
++ if (!document.isObject()) {
++ d_ptr->deserializer.setInvalidFormatError();
++ return false;
++ }
++
++ 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;
++ }
++
++ bool ProtobufScalarJsonSerializers::validateJsonNumberString(const QString &input)
++ {
++ static const QRegularExpression NumberValidator("^-?\\d+$"_L1);
++ return NumberValidator.match(input).hasMatch();
++ }
++
++ QT_END_NAMESPACE