9#include <QtCore/QJsonArray>
10#include <QtCore/QJsonDocument>
11#include <QtCore/QJsonObject>
13#include <QtCore/QTimeZone>
14#include <QtCore/QUrlQuery>
15#include <QtCore/QVector>
27inline void editSubobject(QJsonObject& json,
auto key, std::invocable<QJsonObject&>
auto visitor)
29 auto subObject = json.take(key).toObject();
31 json.insert(key, subObject);
34inline void replaceSubvalue(QJsonObject& json,
auto topLevelKey,
auto subKey, QJsonValue subValue)
36 editSubobject(json, topLevelKey, [subKey, subValue](QJsonObject& innerJson) {
37 innerJson.insert(subKey, subValue);
42struct JsonObjectConverter;
47template <
typename PodT>
56 static T
load(
const QJsonValue& jv) {
return fromJson<T>(jv.toObject()); }
57 static T
load(
const QJsonDocument& jd) {
return fromJson<T>(jd.object()); }
77 static_assert(std::is_class_v<T> || std::is_union_v<T>,
78 "The default JsonConverter definition only supports class types; "
79 "check the constraints on the specialisation you intended to invoke");
81 static auto dump(
const T& data)
83 if constexpr (requires() { data.toJson(); })
87 JsonObjectConverter<T>::dumpTo(jo, data);
93 static T
load(
const QJsonObject& jo)
97 if constexpr (std::is_same_v<T, QJsonObject>)
99 else if constexpr (std::is_constructible_v<T, QJsonObject>)
103 JsonObjectConverter<T>::fillFrom(jo, pod);
114 if constexpr (std::is_constructible_v<QJsonValue, T>)
121inline void fillJson(QJsonObject& json,
const T& data)
123 JsonObjectConverter<T>::dumpTo(json, data);
126template <
typename PodT>
138template <
typename PodT>
141 pod = fromJson<PodT>(json);
147 if constexpr (requires() { JsonObjectConverter<T>::fillFrom({}, pod); }) {
148 JsonObjectConverter<T>::fillFrom(jv.toObject(), pod);
150 }
else if (!jv.isUndefined())
151 pod = fromJson<T>(jv);
155 QUOTIENT_API void reportEnumOutOfBounds(uint32_t v,
const char* enumTypeName);
158template <
typename EnumT>
161 static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
162 if (
const auto it = std::ranges::find(enumValues, s); it != cend(enumValues))
163 return static_cast<EnumT>(it - cbegin(enumValues));
168template <
typename EnumT>
171 static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
172 const auto intV = std::to_underlying(v);
174 return enumValues[intV];
176 _impl::reportEnumOutOfBounds(intV, qt_getEnumName(EnumT()));
180template <
typename FlagT>
184 static_assert(std::is_unsigned_v<std::underlying_type_t<FlagT>>);
185 if (
const auto it = std::ranges::find(flagValues, s); it != cend(flagValues))
186 return static_cast<FlagT>(1U << (it - cbegin(flagValues)));
191template <
typename FlagT>
194 static_assert(std::is_unsigned_v<std::underlying_type_t<FlagT>>);
195 const auto intV = std::to_underlying(v);
196 if (
const auto offset = std::countr_zero(intV);
QUO_CHECK(offset < ssize(flagValues)))
197 return flagValues[offset];
199 _impl::reportEnumOutOfBounds(intV, qt_getEnumName(FlagT()));
206inline bool fromJson(
const QJsonValue& jv) {
return jv.toBool(); }
209inline int fromJson(
const QJsonValue& jv) {
return jv.toInt(); }
212inline double fromJson(
const QJsonValue& jv) {
return jv.toDouble(); }
215inline float fromJson(
const QJsonValue& jv) {
return float(jv.toDouble()); }
282 return val.isValid() ? val.toMSecsSinceEpoch() : QJsonValue();
287 return QDateTime::fromMSecsSinceEpoch(fromJson<qint64>(jv), QTimeZone::UTC);
294 return fromJson<QDateTime>(jv).date();
319template <
typename... Ts>
326 [](
const auto& value) {
return QJsonValue { toJson(value) }; }, v);
341 static QJsonValue dump(
const std::optional<T>& from)
343 return from.has_value() ? toJson(*from) : QJsonValue();
345 static std::optional<T> load(
const QJsonValue& jv)
347 if (jv.isUndefined() || jv.isNull())
349 return { fromJson<T>(jv) };
353template <
typename ContT>
355 static auto dump(
const ContT& vals)
358 for (
const auto& v : vals)
359 ja.push_back(toJson(v));
362 static auto load(
const QJsonArray& ja)
365 vals.reserve(
static_cast<
typename ContT::size_type>(ja.size()));
370 for (
const auto& v : ja)
371 vals.push_back(fromJson<
typename ContT::value_type, QJsonValue>(v));
374 static auto load(
const QJsonValue& jv) {
return load(jv.toArray()); }
375 static auto load(
const QJsonDocument& jd) {
return load(jd.array()); }
382template <
typename T, size_t N>
391 static constexpr std::make_index_sequence<N> Indices{};
392 template <
typename TargetT, size_t... I>
393 static auto staticTransform(
const auto& source, std::index_sequence<I...>,
396 return TargetT { unaryFn(source[I])... };
398 static auto dump(
const std::array<T, N> a)
400 return staticTransform<QJsonArray>(a, Indices, [](
const T& v) {
404 static auto load(
const QJsonArray& ja)
406 return staticTransform<std::array<T, N>>(ja, Indices,
407 fromJson<T, QJsonValue>);
426 for (
const auto&
e :
s)
437template <
typename HashMapT>
439 static void dumpTo(QJsonObject& json,
const HashMapT& hashMap)
441 for (
auto it = hashMap.begin(); it != hashMap.end(); ++it)
442 json.insert(it.key(), toJson(it.value()));
444 static void fillFrom(
const QJsonObject& jo, HashMapT& h)
446 h.reserve(h.size() + jo.size());
449 for (
auto it = jo.begin(); it != jo.end(); ++it)
450 h[it.key()] = fromJson<
typename HashMapT::mapped_type, QJsonValue>(
455template <
typename T,
typename HashT>
472 template <
typename KeyT,
typename ValT>
473 inline void addTo(QJsonObject& o, KeyT&& k, ValT&& v)
475 o.insert(std::forward<KeyT>(k), toJson(std::forward<ValT>(v)));
478 inline void addTo(QUrlQuery& q,
const QString& k,
auto v)
479 requires requires { u"%1"_s.arg(v); }
481 q.addQueryItem(k, u"%1"_s.arg(v));
486 inline void addTo(QUrlQuery& q,
const QString& k,
bool v)
488 q.addQueryItem(k, v ? u"true"_s : u"false"_s);
491 inline void addTo(QUrlQuery& q,
const QString& k,
const QUrl& v)
493 q.addQueryItem(k, QString::fromLatin1(v.toEncoded()));
496 inline void addTo(QUrlQuery& q,
const QString& k,
const QStringList& vals)
498 for (
const auto& v : vals)
499 q.addQueryItem(k, v);
502 template <
typename ValT>
503 inline void addTo(QUrlQuery& q,
const QString&,
const QHash<QString, ValT>& fields)
505 for (
const auto& [k, v] : fields.asKeyValueRange())
511 template <
typename ValT,
bool Force =
true>
513 template <
typename KeyT,
typename ForwardedT>
514 static void impl(
auto& container, KeyT&& key, ForwardedT&& value)
516 addTo(container, std::forward<KeyT>(key), std::forward<ForwardedT>(value));
521 template <
typename ValT>
522 requires requires(ValT v) { v.isEmpty(); }
523 struct AddNode<ValT, IfNotEmpty> {
524 template <
typename KeyT,
typename ForwardedT>
525 static void impl(
auto& container, KeyT&& key, ForwardedT&& value)
527 if (!value.isEmpty())
528 addTo(container, std::forward<KeyT>(key), std::forward<ForwardedT>(value));
533 template <
typename ValT>
534 struct AddNode<std::optional<ValT>, IfNotEmpty> {
535 template <
typename KeyT>
536 static void impl(
auto& container, KeyT&& key,
const auto& optValue)
539 addTo(container, std::forward<KeyT>(key), *optValue);
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567template <
bool Force =
true,
typename KeyT,
typename ValT>
568inline void addParam(
auto& container, KeyT&& key, ValT&& value)
570 _impl::AddNode<std::decay_t<ValT>, Force>::impl(container, std::forward<KeyT>(key),
571 std::forward<ValT>(value));
579 QString result { s };
580 for (
auto it = result.begin(); it != result.end(); ++it)
582 const auto offset =
static_cast<
int>(it - result.begin());
583 result.insert(offset, u'_');
584 it = result.begin() + offset + 1;
auto toSnakeCase(QLatin1String s)
QString flagToJsonString(FlagT v, const auto &flagValues)
QString enumToJsonString(EnumT v, const auto &enumValues)
void fillJson(QJsonObject &json, const T &data)
void fromJson(const auto &json, PodT &pod)
QJsonValue toJson(const QDateTime &val)
auto toJson(const T &pod)
void fillFromJson(const QJsonValue &jv, T &pod)
void addParam(auto &container, KeyT &&key, ValT &&value)
std::optional< EnumT > enumFromJsonString(const QString &s, const auto &enumValues)
QJsonValue toJson(const std::variant< Ts... > &v)
void editSubobject(QJsonObject &json, auto key, std::invocable< QJsonObject & > auto visitor)
void replaceSubvalue(QJsonObject &json, auto topLevelKey, auto subKey, QJsonValue subValue)
bool fromJson(const QJsonValue &jv)
std::optional< FlagT > flagFromJsonString(const QString &s, const auto &flagValues)
PodT fromJson(const auto &)
constexpr bool IfNotEmpty
static void dumpTo(QJsonObject &json, const HashMapT &hashMap)
static void fillFrom(const QJsonObject &jo, HashMapT &h)
static auto dump(const ContT &vals)
static auto load(const QJsonArray &ja)
The switchboard for extra conversion algorithms behind from/toJson.
static T load(const QJsonObject &jo)
static auto dump(const T &data)
static T load(const QJsonValue &jv)
#define QUO_CHECK(...)
Evaluate the boolean expression and, in Debug mode, assert it to be true.