1// Copyright (C) 2021 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 QTYPEDJSON_H
5#define QTYPEDJSON_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/QObject>
19#include <QtCore/QJsonValue>
20#include <QtCore/QJsonObject>
21#include <QtCore/QScopeGuard>
22#include <QtCore/QSet>
23#include <QtCore/QByteArray>
24#include <QtCore/QMetaEnum>
25#include <QtCore/QLoggingCategory>
26#include <QtCore/qjsonvalue.h>
27#include <QtCore/qjsonarray.h>
28#include <QtCore/qjsonobject.h>
29#include <QtJsonRpc/qtjsonrpcglobal.h>
30
31#include <functional>
32#include <memory>
33#include <typeinfo>
34#include <optional>
35
36QT_BEGIN_NAMESPACE
37
38namespace QTypedJson {
39Q_JSONRPC_EXPORT Q_DECLARE_LOGGING_CATEGORY(jsonRpcLog);
40
41Q_NAMESPACE
42
43enum class ObjectOption { None = 0, KeepExtraFields = 1, WarnExtra = 2 };
44Q_ENUM_NS(ObjectOption)
45Q_DECLARE_FLAGS(ObjectOptions, ObjectOption)
46Q_DECLARE_OPERATORS_FOR_FLAGS(ObjectOptions)
47
48enum class ParseMode { StopOnError };
49Q_ENUM_NS(ParseMode)
50
51enum class ParseStatus { Normal, Failed };
52Q_ENUM_NS(ParseStatus)
53
54template<typename... Ts>
55using void_t = void;
56
57template<typename T, typename = void>
58struct HasTypeName : std::false_type
59{
60};
61
62template<typename T>
63struct HasTypeName<T, void_t<decltype(T::TypeName)>> : std::true_type
64{
65};
66
67template<typename T, typename = void>
68struct HasMetaEnum : std::false_type
69{
70};
71
72template<typename T>
73struct HasMetaEnum<T, void_t<decltype(QMetaEnum::fromType<T>())>> : std::true_type
74{
75};
76
77template<typename T>
78const char *typeName()
79{
80 if constexpr (HasTypeName<T>::value)
81 return T::TypeName;
82 else
83 return typeid(T).name();
84}
85
86template<typename T, typename = void>
87struct JsonObjectOptions
88{
89 static constexpr ObjectOptions value = ObjectOption::None;
90};
91
92template<typename T>
93struct JsonObjectOptions<T, void_t<decltype(T::jsonObjectOptions)>> : std::true_type
94{
95 static constexpr ObjectOptions value = T::jsonObjectOptions;
96};
97
98template<typename T, typename = void>
99struct HasExtraFields : std::false_type
100{
101};
102
103template<typename T>
104struct HasExtraFields<T, void_t<decltype(std::declval<T>().extraFields())>> : std::true_type
105{
106};
107
108template<typename T, typename = void>
109struct SetExtraFields : std::false_type
110{
111};
112
113template<typename T>
114struct SetExtraFields<
115 T, void_t<decltype(std::declval<T>().setExtraFields(std::declval<QJsonObject>()))>>
116 : std::true_type
117{
118};
119
120template<typename T, typename = void>
121struct IsList : std::false_type
122{
123};
124
125template<typename T>
126struct IsList<T, void_t<typename T::value_type>> : std::true_type
127{
128};
129
130template<typename T>
131struct IsPointer : std::is_pointer<T>
132{
133};
134
135template<typename T>
136struct IsPointer<std::shared_ptr<T>> : std::true_type
137{
138};
139
140template<typename T>
141struct IsPointer<std::unique_ptr<T>> : std::true_type
142{
143};
144
145template<typename T>
146struct IsVariant : std::false_type
147{
148};
149
150template<typename... Args>
151struct IsVariant<std::variant<Args...>> : std::true_type
152{
153};
154
155template<typename T>
156inline QString enumToString(T value)
157{
158 int iValue = int(value);
159 if constexpr (HasMetaEnum<T>::value) {
160 QMetaEnum metaEnum = QMetaEnum::fromType<T>();
161 for (int i = 0; i < metaEnum.keyCount(); ++i) {
162 if (iValue == metaEnum.value(index: i))
163 return QString::fromUtf8(utf8: metaEnum.key(index: i));
164 }
165 }
166 return QString::number(iValue);
167}
168
169template<typename T>
170inline T enumFromString(const QString &value)
171{
172 bool ok;
173 int v = value.toInt(ok: &ok);
174 if (ok)
175 return T(v);
176 if constexpr (HasMetaEnum<T>::value) {
177 QMetaEnum metaEnum = QMetaEnum::fromType<T>();
178 for (int i = 0; i < metaEnum.keyCount(); ++i) {
179 if (value.compare(other: QLatin1String(metaEnum.key(index: i)), cs: Qt::CaseInsensitive) == 0)
180 return T { metaEnum.value(index: i) };
181 }
182 }
183 return T {};
184}
185
186template<typename T>
187inline QString enumToIntString(T value)
188{
189 return QString::number(int(value));
190}
191
192template<typename T>
193inline T enumFromIntString(const QString &value)
194{
195 bool ok;
196 int v = value.toInt(ok: &ok);
197 if (ok)
198 return T(v);
199 return T {};
200}
201
202class Q_JSONRPC_EXPORT ValueStack
203{
204public:
205 QJsonValue value;
206 QString fieldPath;
207 int indexPath = -1;
208 int warnLevel = 0;
209};
210
211class ObjectStack
212{
213public:
214 const char *type;
215 ObjectOptions options;
216 QSet<QString> visitedFields;
217};
218
219class ReaderPrivate
220{
221public:
222 QList<ValueStack> valuesStack = {};
223 QList<ObjectStack> objectsStack = {};
224 ObjectOptions baseOptions = {};
225 ParseMode parseMode = ParseMode::StopOnError;
226 ParseStatus parseStatus = ParseStatus::Normal;
227 QStringList errorMessages = {};
228};
229
230class Q_JSONRPC_EXPORT Reader
231{
232public:
233 Reader(const QJsonValue &v);
234 ~Reader();
235
236 QStringList errorMessages();
237 void clearErrorMessages();
238
239 // serialization templates
240
241 template<typename T>
242 bool startObject(const char *type, ObjectOptions options, quintptr id, T &)
243 {
244 return this->startObjectF(type, options, id);
245 }
246 template<typename T>
247 void endObject(const char *type, ObjectOptions options, quintptr id, T &obj);
248
249 template<typename T>
250 bool startArray(qint32 &size, T &el)
251 {
252 startArrayF(size);
253 using BaseT = std::decay_t<T>;
254 if constexpr (std::is_base_of_v<QList<typename BaseT::value_type>, BaseT>) {
255 el.resize(size);
256 } else {
257 assert(false); // currently unsupported
258 }
259 return true;
260 }
261
262 template<typename T>
263 bool handleOptional(T &el)
264 {
265 bool isMissing = currentValue().isUndefined() || currentValue().isNull();
266 if (isMissing)
267 el.reset();
268 else
269 el.emplace();
270 return bool(el);
271 }
272
273 template<typename T>
274 bool handlePointer(T &el)
275 {
276 bool isMissing = currentValue().isUndefined() || currentValue().isNull();
277 if (isMissing)
278 el = nullptr;
279 else
280 el = T(new std::decay_t<decltype(*el)>);
281 return bool(el);
282 }
283
284 template<typename... T>
285 void handleVariant(std::variant<T...> &el)
286 {
287 std::tuple<T...> options;
288 int status = 0;
289 ReaderPrivate origStatus = *m_p;
290 QStringList err;
291 auto tryRead = [this, &origStatus, &status, &el, &err](auto &x) {
292 if (status == 2)
293 return;
294 if (status == 1)
295 *this->m_p = origStatus;
296 else
297 status = 1;
298 doWalk(*this, x);
299 if (m_p->parseStatus == ParseStatus::Normal) {
300 status = 2;
301 el = x;
302 return;
303 }
304 err.append(QStringLiteral(u"Type %1 failed with errors:")
305 .arg(a: QLatin1String(typeid(decltype(x)).name())));
306 err += m_p->errorMessages;
307 };
308 std::apply([&tryRead](auto &...x) { (..., tryRead(x)); }, options);
309 if (status == 1) {
310 m_p->errorMessages.clear();
311 m_p->errorMessages.append(QStringLiteral(u"All options of variant failed:"));
312 m_p->errorMessages += err;
313 }
314 }
315
316 template<typename T>
317 void handleEnum(T &e)
318 {
319 if (currentValue().isDouble()) {
320 e = T(currentValue().toInt());
321 } else {
322 e = enumFromString<T>(currentValue().toString());
323 }
324 }
325
326 template<typename T>
327 void endArray(qint32 &size, T &)
328 {
329 this->endArrayF(size);
330 }
331
332 // serialization callbacks
333 void handleBasic(bool &);
334 void handleBasic(QByteArray &);
335 void handleBasic(int &);
336 void handleBasic(double &);
337 void handleNullType();
338 void handleJson(QJsonValue &v);
339 void handleJson(QJsonObject &v);
340 void handleJson(QJsonArray &v);
341 bool startField(const QString &fieldName);
342 bool startField(const char *fieldName);
343 void endField(const QString &fieldName);
344 void endField(const char *fieldName);
345 bool startElement(qint32 index);
346 void endElement(qint32 index);
347 bool startTuple(qint32 size);
348 void endTuple(qint32 size);
349
350private:
351 void warnExtra(const QJsonObject &e);
352 void warnMissing(QStringView s);
353 void warnNonNull();
354 void warnInvalidSize(qint32 size, qint32 expectedSize);
355 void warn(const QString &msg);
356 QJsonObject getExtraFields() const;
357 bool startObjectF(const char *type, ObjectOptions options, quintptr id);
358 void endObjectF(const char *type, ObjectOptions options, quintptr id);
359 void startArrayF(qint32 &size);
360 void endArrayF(qint32 &size);
361 bool hasElement();
362 QString currentPath() const;
363 const QJsonValue &currentValue() const { return m_p->valuesStack.last().value; }
364 ReaderPrivate *m_p;
365};
366
367template<typename T, typename = void>
368struct HasWalk : std::false_type
369{
370};
371
372template<typename T>
373struct HasWalk<T, void_t<decltype(T {}.walk(std::declval<Reader>))>> : std::true_type
374{
375};
376
377template<typename W, typename C, typename T>
378void field(W &w, const C &fieldName, T &el)
379{
380 if (w.startField(fieldName)) {
381 auto guard = qScopeGuard([&w, &fieldName]() { w.endField(fieldName); });
382 doWalk(w, el);
383 }
384}
385
386template<typename W, typename T>
387inline void doWalk(W &w, T &el)
388{
389 using BaseT = std::decay_t<T>;
390 if constexpr (
391 std::is_same_v<
392 BaseT,
393 int> || std::is_same_v<BaseT, double> || std::is_same_v<BaseT, bool> || std::is_same_v<BaseT, QByteArray>) {
394 w.handleBasic(el);
395 } else if constexpr (HasWalk<BaseT>::value) {
396 const char *type = typeName<BaseT>();
397 ObjectOptions options = JsonObjectOptions<BaseT>::value;
398 quintptr id = quintptr(&el);
399 if (w.startObject(type, options, id, el)) {
400 el.walk(w);
401 w.endObject(type, options, id, el);
402 }
403 } else if constexpr (
404 std::is_same_v<
405 BaseT,
406 QJsonValue> || std::is_same_v<BaseT, QJsonObject> || std::is_same_v<BaseT, QJsonArray>) {
407 w.handleJson(el);
408 } else if constexpr (std::is_enum_v<BaseT>) {
409 w.handleEnum(el);
410 } else if constexpr (std::is_same_v<std::nullptr_t, BaseT>) {
411 w.handleNullType();
412 } else if constexpr (IsPointer<BaseT>::value) {
413 if (w.handlePointer(el) && el)
414 doWalk(w, *el);
415 } else if constexpr (IsVariant<BaseT>::value) {
416 w.handleVariant(el);
417 } else if constexpr (IsList<BaseT>::value) {
418 if constexpr (std::is_same_v<std::optional<typename BaseT::value_type>, BaseT>) {
419 if (w.handleOptional(el) && el)
420 doWalk(w, *el);
421 } else {
422 int size = el.size();
423 if (!w.startArray(size, el))
424 return;
425 int i = 0;
426 for (auto &subEl : el) {
427 if (!w.startElement(i))
428 break;
429 doWalk(w, subEl);
430 w.endElement(i);
431 ++i;
432 }
433 w.endArray(size, el);
434 }
435 } else {
436 qWarning() << "Unhandled type" << typeid(T).name();
437 assert(false);
438 }
439}
440
441template<typename T>
442inline void Reader::endObject(const char *type, ObjectOptions options, quintptr id, T &obj)
443{
444 using BaseT = std::decay_t<T>;
445 QJsonObject extra;
446 if (SetExtraFields<BaseT>::value
447 || (options & (ObjectOption::KeepExtraFields | ObjectOption::WarnExtra)))
448 extra = this->getExtraFields();
449 this->endObjectF(type, options, id);
450 if constexpr (SetExtraFields<BaseT>::value)
451 obj.setExtraFields(extra);
452 else if (extra.constBegin() != extra.constEnd())
453 warnExtra(e: extra);
454}
455
456class Q_JSONRPC_EXPORT JsonBuilder
457{
458public:
459 JsonBuilder() = default;
460
461 // public api
462 QJsonValue popLastValue();
463
464 // serialization templates
465 template<typename T>
466 bool handleOptional(T &el)
467 {
468 if (el)
469 return true;
470 this->handleMissingOptional();
471 return false;
472 }
473
474 template<typename T>
475 bool handlePointer(T &el)
476 {
477 return bool(el);
478 }
479
480 template<typename T>
481 bool startObject(const char *type, ObjectOptions options, quintptr id, T &)
482 {
483 return this->startObjectF(type, options, id);
484 }
485
486 template<typename T>
487 void endObject(const char *type, ObjectOptions options, quintptr id, T &)
488 {
489 this->endObjectF(type, options, id);
490 }
491
492 template<typename T>
493 bool startArray(qint32 &size, T &el)
494 {
495 using BaseT = std::decay_t<T>;
496 if constexpr (std::is_base_of_v<QList<typename BaseT::value_type>, BaseT>) {
497 size = el.size();
498 } else {
499 assert(false); // currently unsupported
500 }
501 return startArrayF(size);
502 }
503
504 template<typename T>
505 void endArray(qint32 &size, T &)
506 {
507 this->endArrayF(size);
508 }
509
510 template<typename T>
511 void handleVariant(T &el)
512 {
513 std::visit([this](auto &v) { doWalk(*this, v); }, el);
514 }
515
516 template<typename T>
517 void handleEnum(T &el)
518 {
519 QString eVal = enumToString(el);
520 bool ok;
521 int value = eVal.toInt(ok: &ok);
522 if (ok)
523 this->handleBasic(v: value);
524 else
525 this->handleBasic(v: eVal.toUtf8());
526 }
527
528 // serialization callbacks
529 void handleBasic(const bool &v);
530 void handleBasic(const QByteArray &v);
531 void handleBasic(const int &v);
532 void handleBasic(const double &v);
533 void handleNullType();
534 void handleJson(QJsonValue &v);
535 void handleJson(QJsonObject &v);
536 void handleJson(QJsonArray &v);
537 bool startField(const QString &fieldName);
538 bool startField(const char *fieldName);
539 void endField(const QString &);
540 void endField(const char *);
541 bool startElement(qint32 index);
542 void endElement(qint32);
543 bool startTuple(qint32 size);
544 void endTuple(qint32 size);
545
546private:
547 void handleMissingOptional();
548 bool startObjectF(const char *, ObjectOptions, quintptr);
549 void endObjectF(const char *, ObjectOptions, quintptr);
550 bool startArrayF(qint32 &);
551 void endArrayF(qint32 &);
552
553 QList<qsizetype> m_fieldLevel;
554 QList<qsizetype> m_arrayLevel;
555 QList<std::variant<QJsonObject, QJsonArray, QJsonValue>> m_values;
556};
557
558template<typename W, typename... Params>
559void doWalkArgs(W &w, Params... params)
560{
561 if constexpr (sizeof...(Params) == 0) {
562 } else if constexpr (sizeof...(Params) == 1) {
563 doWalk(w, params...);
564 } else {
565 if (!w.startTuple(sizeof...(Params)))
566 return;
567 qint32 i = 0;
568 bool skipRest = false;
569 std::apply(
570 [&i, &w, &skipRest](auto &el) {
571 if (skipRest || !w.startElement(i)) {
572 skipRest = true;
573 } else {
574 doWalk(w, el);
575 w.endElement(i++);
576 }
577 },
578 params...);
579 w.endTuple(sizeof...(Params));
580 }
581}
582
583template<typename... Params>
584QJsonValue toJsonValue(Params... params)
585{
586 JsonBuilder b;
587 doWalkArgs(b, params...);
588 return b.popLastValue();
589}
590
591} // namespace QTypedJson
592QT_END_NAMESPACE
593#endif // QTYPEDJSON_H
594

source code of qtlanguageserver/src/jsonrpc/qtypedjson_p.h