// 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 PROTOBUFSCALARJSONSERIALIZERS_P_H #define PROTOBUFSCALARJSONSERIALIZERS_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 #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace ProtobufScalarJsonSerializers { // Tests if V is JSON compatible integer // int32 | int64 | uint32 | sint32 | sint64 | fixed32 | sfixed32 | sfixed64 template constexpr inline bool IsJsonInt = false; template <> constexpr inline bool IsJsonInt = true; template <> constexpr inline bool IsJsonInt = true; template <> constexpr inline bool IsJsonInt = true; template <> constexpr inline bool IsJsonInt = true; template <> constexpr inline bool IsJsonInt = true; template <> constexpr inline bool IsJsonInt = true; template <> constexpr inline bool IsJsonInt = true; template <> constexpr inline bool IsJsonInt = true; template using if_json_int = std::enable_if_t, bool>; // Tests if V is JSON incompatible 64-bit unsigned integer // uint64 | fixed64 template constexpr inline bool IsJsonInt64 = false; template <> constexpr inline bool IsJsonInt64 = true; template <> constexpr inline bool IsJsonInt64 = true; template using if_json_int64 = std::enable_if_t, bool>; // Tests if V is JSON compatible floating point number // float | double template constexpr inline bool IsJsonFloatingPoint = false; template <> constexpr inline bool IsJsonFloatingPoint = true; template <> constexpr inline bool IsJsonFloatingPoint = true; template using if_json_floating_point = std::enable_if_t, bool>; // Special value strings constexpr inline QLatin1StringView Infinity("Infinity"); constexpr inline QLatin1StringView NegInfinity("-Infinity"); constexpr inline QLatin1StringView NaN("NaN"); constexpr inline QByteArrayView InfinityLower("infinity"); constexpr inline QByteArrayView NegInfinityLower("-infinity"); constexpr inline QByteArrayView NaNLower("nan"); constexpr inline QLatin1StringView True("true"); constexpr inline QLatin1StringView False("false"); template = true> QJsonValue serialize(T propertyValue) { return QJsonValue(qint64(propertyValue)); } template = true> QJsonValue serialize(T propertyValue) { return QJsonValue(QString::number(propertyValue)); } template = true> QJsonValue serialize(T propertyValue) { if (propertyValue == -std::numeric_limits::infinity()) return QJsonValue(NegInfinity); if (propertyValue == std::numeric_limits::infinity()) return QJsonValue(Infinity); if (propertyValue != propertyValue) return QJsonValue(NaN); return QJsonValue(propertyValue); } inline QJsonValue serialize(bool propertyValue) { return QJsonValue(propertyValue); } inline QJsonValue serialize(const QString &propertyValue) { return QJsonValue(propertyValue); } inline QJsonValue serialize(const QByteArray &propertyValue) { return QJsonValue(QString::fromUtf8(propertyValue.toBase64())); } template QJsonValue serializeList(const QVariant &propertyValue) { QJsonArray arr; L listValue = propertyValue.value(); for (const auto &value : listValue) arr.append(serialize(value)); return QJsonValue(arr); } template QJsonValue serializeCommon(const QVariant &propertyValue) { return serialize(propertyValue.value()); } Q_PROTOBUF_EXPORT bool validateJsonNumberString(const QString &input); template = true> T deserialize(const QJsonValue &value, bool &ok) { auto variantValue = value.toVariant(); qint64 raw = 0; switch (variantValue.metaType().id()) { case QMetaType::QString: if (!validateJsonNumberString(variantValue.toString())) ok = false; else raw = variantValue.toLongLong(&ok); break; case QMetaType::LongLong: raw = variantValue.toLongLong(&ok); break; case QMetaType::Double: { double d = value.toDouble(); ok = convertDoubleTo(d, &raw) && double(raw) == d; } break; default: break; } // For types that "smaller" than qint64 we need to check if the value fits its limits range if constexpr (sizeof(T) != sizeof(qint64)) { constexpr auto Limits = []() { if constexpr (std::is_same_v || std::is_same_v) return std::numeric_limits{}; else if constexpr (std::is_same_v) return std::numeric_limits{}; else return std::numeric_limits{}; }(); ok = ok && (raw >= Limits.min() && raw <= Limits.max()); } return ok ? T(raw) : T{}; } template = true> T deserialize(const QJsonValue &value, bool &ok) { quint64 raw = 0; auto variantValue = value.toVariant(); switch (variantValue.metaType().id()) { case QMetaType::QString: case QMetaType::LongLong: // Here we attempt converting the value to ULongLong raw = variantValue.toULongLong(&ok); break; case QMetaType::Double: { double d = value.toDouble(); ok = convertDoubleTo(d, &raw) && double(raw) == d; } break; default: break; } return T(raw); } template = true> T deserialize(const QJsonValue &value, bool &ok) { ok = true; QByteArray data = value.toVariant().toByteArray(); if (data.compare(NegInfinityLower, Qt::CaseInsensitive) == 0) return -std::numeric_limits::infinity(); if (data.compare(InfinityLower, Qt::CaseInsensitive) == 0) return std::numeric_limits::infinity(); if (data.compare(NaNLower, Qt::CaseInsensitive) == 0) return T(NAN); if constexpr (std::is_same_v) return data.toFloat(&ok); else return data.toDouble(&ok); } template , bool> = true> bool deserialize(const QJsonValue &value, bool &ok) { if (value.isBool()) { ok = true; return value.toBool(); } else if (value.isString()) { if (value.toString() == True) { ok = true; return true; } else if (value.toString() == False) { ok = true; return false; } } return false; } template , bool> = true> QString deserialize(const QJsonValue &value, bool &ok) { ok = value.isString(); return value.toString(); } template , bool> = true> QByteArray deserialize(const QJsonValue &value, bool &ok) { QByteArray data = value.toVariant().toByteArray(); if (value.isString()) { ok = true; return QByteArray::fromBase64(data); } return {}; } template QVariant deserializeList(const QJsonValue &value, bool &ok) { if (!value.isArray()) { ok = false; return {}; } L list; QJsonArray array = value.toArray(); for (auto arrayValue : array) { ok = false; T value = deserialize(arrayValue, ok); if (!ok) break; list.append(value); } return QVariant::fromValue(list); } template QVariant deserializeCommon(const QJsonValue &value, bool &ok) { ok = false; return QVariant::fromValue(deserialize(value, ok)); } } // namespace ProtobufScalarJsonSerializers QT_END_NAMESPACE #endif // PROTOBUFSCALARJSONSERIALIZERS_P_H