1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef PROTOBUFSCALARJSONSERIALIZERS_P_H
5#define PROTOBUFSCALARJSONSERIALIZERS_P_H
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <QtProtobuf/qtprotobufexports.h>
18#include <QtProtobuf/qtprotobuftypes.h>
19
20#include <QtCore/private/qnumeric_p.h>
21#include <QtCore/qjsonarray.h>
22#include <QtCore/qjsonvalue.h>
23#include <QtCore/qtconfigmacros.h>
24
25#include <limits>
26
27QT_BEGIN_NAMESPACE
28
29namespace ProtobufScalarJsonSerializers {
30// Tests if V is JSON compatible integer
31// int32 | int64 | uint32 | sint32 | sint64 | fixed32 | sfixed32 | sfixed64
32template <typename T>
33constexpr inline bool IsJsonInt = false;
34template <>
35constexpr inline bool IsJsonInt<QtProtobuf::int32> = true;
36template <>
37constexpr inline bool IsJsonInt<QtProtobuf::int64> = true;
38template <>
39constexpr inline bool IsJsonInt<QtProtobuf::uint32> = true;
40template <>
41constexpr inline bool IsJsonInt<QtProtobuf::sint32> = true;
42template <>
43constexpr inline bool IsJsonInt<QtProtobuf::sint64> = true;
44template <>
45constexpr inline bool IsJsonInt<QtProtobuf::fixed32> = true;
46template <>
47constexpr inline bool IsJsonInt<QtProtobuf::sfixed32> = true;
48template <>
49constexpr inline bool IsJsonInt<QtProtobuf::sfixed64> = true;
50
51template <typename T>
52using if_json_int = std::enable_if_t<IsJsonInt<T>, bool>;
53
54// Tests if V is JSON incompatible 64-bit unsigned integer
55// uint64 | fixed64
56template <typename T>
57constexpr inline bool IsJsonInt64 = false;
58template <>
59constexpr inline bool IsJsonInt64<QtProtobuf::uint64> = true;
60template <>
61constexpr inline bool IsJsonInt64<QtProtobuf::fixed64> = true;
62
63template <typename T>
64using if_json_int64 = std::enable_if_t<IsJsonInt64<T>, bool>;
65
66// Tests if V is JSON compatible floating point number
67// float | double
68template <typename T>
69constexpr inline bool IsJsonFloatingPoint = false;
70template <>
71constexpr inline bool IsJsonFloatingPoint<float> = true;
72template <>
73constexpr inline bool IsJsonFloatingPoint<double> = true;
74
75template <typename T>
76using if_json_floating_point = std::enable_if_t<IsJsonFloatingPoint<T>, bool>;
77
78// Special value strings
79constexpr inline QLatin1StringView Infinity("Infinity");
80constexpr inline QLatin1StringView NegInfinity("-Infinity");
81constexpr inline QLatin1StringView NaN("NaN");
82
83constexpr inline QByteArrayView InfinityLower("infinity");
84constexpr inline QByteArrayView NegInfinityLower("-infinity");
85constexpr inline QByteArrayView NaNLower("nan");
86
87constexpr inline QLatin1StringView True("true");
88constexpr inline QLatin1StringView False("false");
89
90template <typename T, if_json_int<T> = true>
91QJsonValue serialize(T propertyValue)
92{
93 return QJsonValue(qint64(propertyValue));
94}
95
96template <typename T, if_json_int64<T> = true>
97QJsonValue serialize(T propertyValue)
98{
99 return QJsonValue(QString::number(propertyValue));
100}
101
102template <typename T, if_json_floating_point<T> = true>
103QJsonValue serialize(T propertyValue)
104{
105 if (propertyValue == -std::numeric_limits<T>::infinity())
106 return QJsonValue(NegInfinity);
107
108 if (propertyValue == std::numeric_limits<T>::infinity())
109 return QJsonValue(Infinity);
110
111 if (propertyValue != propertyValue)
112 return QJsonValue(NaN);
113
114 return QJsonValue(propertyValue);
115}
116
117inline QJsonValue serialize(bool propertyValue)
118{
119 return QJsonValue(propertyValue);
120}
121
122inline QJsonValue serialize(const QString &propertyValue)
123{
124 return QJsonValue(propertyValue);
125}
126
127inline QJsonValue serialize(const QByteArray &propertyValue)
128{
129 return QJsonValue(QString::fromUtf8(ba: propertyValue.toBase64()));
130}
131
132template <typename L>
133QJsonValue serializeList(const QVariant &propertyValue)
134{
135 QJsonArray arr;
136 L listValue = propertyValue.value<L>();
137 for (const auto &value : listValue)
138 arr.append(value: serialize(value));
139
140 return QJsonValue(arr);
141}
142
143template <typename T>
144QJsonValue serializeCommon(const QVariant &propertyValue)
145{
146 return serialize(propertyValue.value<T>());
147}
148
149Q_PROTOBUF_EXPORT bool validateJsonNumberString(const QString &input);
150
151template <typename T, if_json_int<T> = true>
152T deserialize(const QJsonValue &value, bool &ok)
153{
154 auto variantValue = value.toVariant();
155 qint64 raw = 0;
156 switch (variantValue.metaType().id()) {
157 case QMetaType::QString:
158 if (!validateJsonNumberString(input: variantValue.toString()))
159 ok = false;
160 else
161 raw = variantValue.toLongLong(ok: &ok);
162 break;
163 case QMetaType::LongLong:
164 raw = variantValue.toLongLong(ok: &ok);
165 break;
166 case QMetaType::Double: {
167 double d = value.toDouble();
168 ok = convertDoubleTo(v: d, value: &raw) && double(raw) == d;
169 } break;
170 default:
171 break;
172 }
173
174 // For types that "smaller" than qint64 we need to check if the value fits its limits range
175 if constexpr (sizeof(T) != sizeof(qint64)) {
176 if (ok) {
177 if constexpr (std::is_same_v<T, QtProtobuf::sfixed32>
178 || std::is_same_v<T, QtProtobuf::int32>) {
179 using limits = std::numeric_limits<qint32>;
180 ok = raw >= limits::min() && raw <= limits::max();
181 } else if constexpr (std::is_same_v<T, QtProtobuf::fixed32>) {
182 using limits = std::numeric_limits<quint32>;
183 ok = raw >= limits::min() && raw <= limits::max();
184 } else {
185 using limits = std::numeric_limits<T>;
186 ok = raw >= limits::min() && raw <= limits::max();
187 }
188 }
189 }
190
191 return T(raw);
192}
193
194template <typename T, if_json_int64<T> = true>
195T deserialize(const QJsonValue &value, bool &ok)
196{
197 quint64 raw = 0;
198 auto variantValue = value.toVariant();
199 switch (variantValue.metaType().id()) {
200 case QMetaType::QString:
201 case QMetaType::LongLong:
202 // Here we attempt converting the value to ULongLong
203 raw = variantValue.toULongLong(ok: &ok);
204 break;
205 case QMetaType::Double: {
206 double d = value.toDouble();
207 ok = convertDoubleTo(v: d, value: &raw) && double(raw) == d;
208 } break;
209 default:
210 break;
211 }
212 return T(raw);
213}
214
215template <typename T, if_json_floating_point<T> = true>
216T deserialize(const QJsonValue &value, bool &ok)
217{
218 ok = true;
219 QByteArray data = value.toVariant().toByteArray();
220 if (data.compare(a: NegInfinityLower, cs: Qt::CaseInsensitive) == 0)
221 return -std::numeric_limits<T>::infinity();
222
223 if (data.compare(a: InfinityLower, cs: Qt::CaseInsensitive) == 0)
224 return std::numeric_limits<T>::infinity();
225
226 if (data.compare(a: NaNLower, cs: Qt::CaseInsensitive) == 0)
227 return T(NAN);
228
229 if constexpr (std::is_same_v<T, float>)
230 return data.toFloat(ok: &ok);
231 else
232 return data.toDouble(ok: &ok);
233}
234
235template <typename T, std::enable_if_t<std::is_same_v<T, bool>, bool> = true>
236bool deserialize(const QJsonValue &value, bool &ok)
237{
238 if (value.isBool()) {
239 ok = true;
240 return value.toBool();
241 } else if (value.isString()) {
242 if (value.toString() == True) {
243 ok = true;
244 return true;
245 } else if (value.toString() == False) {
246 ok = true;
247 return false;
248 }
249 }
250 return false;
251}
252
253template <typename T, std::enable_if_t<std::is_same_v<T, QString>, bool> = true>
254QString deserialize(const QJsonValue &value, bool &ok)
255{
256 ok = value.isString();
257 return value.toString();
258}
259
260template <typename T, std::enable_if_t<std::is_same_v<T, QByteArray>, bool> = true>
261QByteArray deserialize(const QJsonValue &value, bool &ok)
262{
263 QByteArray data = value.toVariant().toByteArray();
264 if (value.isString()) {
265 ok = true;
266 return QByteArray::fromBase64(base64: data);
267 }
268 return {};
269}
270
271template <typename L /*list*/, typename T /*element*/>
272QVariant deserializeList(const QJsonValue &value, bool &ok)
273{
274 if (!value.isArray()) {
275 ok = false;
276 return {};
277 }
278
279 L list;
280 QJsonArray array = value.toArray();
281 for (auto arrayValue : array) {
282 ok = false;
283 T value = deserialize<T>(arrayValue, ok);
284 if (!ok)
285 break;
286 list.append(value);
287 }
288 return QVariant::fromValue(list);
289}
290
291template <typename T>
292QVariant deserializeCommon(const QJsonValue &value, bool &ok)
293{
294 ok = false;
295 return QVariant::fromValue<T>(deserialize<T>(value, ok));
296}
297
298} // namespace ProtobufScalarJsonSerializers
299QT_END_NAMESPACE
300
301#endif // PROTOBUFSCALARJSONSERIALIZERS_P_H
302

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtgrpc/src/protobuf/protobufscalarjsonserializers_p.h