8 #include <QtCore/QDate>
9 #include <QtCore/QJsonArray>
10 #include <QtCore/QJsonDocument>
11 #include <QtCore/QJsonObject>
12 #include <QtCore/QSet>
13 #include <QtCore/QUrlQuery>
14 #include <QtCore/QVector>
15 #if QT_VERSION >= QT_VERSION_CHECK(6
, 5
, 0
)
16 #include <QtCore/QTimeZone>
19 #include <type_traits>
29 inline void editSubobject(QJsonObject& json,
auto key, std::invocable<QJsonObject&>
auto visitor)
31 auto subObject = json.take(key).toObject();
33 json.insert(key, subObject);
36 inline void replaceSubvalue(QJsonObject& json,
auto topLevelKey,
auto subKey, QJsonValue subValue)
38 editSubobject(json, topLevelKey, [subKey, subValue](QJsonObject& innerJson) {
39 innerJson.insert(subKey, subValue);
44 struct JsonObjectConverter;
49 template <
typename PodT,
typename JsonT>
50 PodT fromJson(
const JsonT&);
53 struct JsonObjectUnpacker {
58 static T load(
const QJsonValue& jv) {
return fromJson<T>(jv.toObject()); }
59 static T load(
const QJsonDocument& jd) {
return fromJson<T>(jd.object()); }
78 struct JsonConverter : JsonObjectUnpacker<T> {
79 static auto dump(
const T& data)
81 if constexpr (requires() { data.toJson(); })
85 JsonObjectConverter<T>::dumpTo(jo, data);
90 using JsonObjectUnpacker<T>::load;
91 static T load(
const QJsonObject& jo)
95 if constexpr (std::is_same_v<T, QJsonObject>)
97 else if constexpr (std::is_constructible_v<T, QJsonObject>)
101 JsonObjectConverter<T>::fillFrom(jo, pod);
107 template <
typename T>
108 inline auto toJson(
const T& pod)
112 if constexpr (std::is_constructible_v<QJsonValue, T>)
115 return JsonConverter<T>::dump(pod);
118 template <
typename T>
119 inline void fillJson(QJsonObject& json,
const T& data)
121 JsonObjectConverter<T>::dumpTo(json, data);
124 template <
typename PodT,
typename JsonT>
125 inline PodT fromJson(
const JsonT& json)
129 return JsonConverter<PodT>::load(json);
136 template <
typename JsonT,
typename PodT>
137 inline void fromJson(
const JsonT& json, PodT& pod)
139 pod = fromJson<PodT>(json);
142 template <
typename T>
143 inline void fillFromJson(
const QJsonValue& jv, T& pod)
145 if constexpr (requires() { JsonObjectConverter<T>::fillFrom({}, pod); }) {
146 JsonObjectConverter<T>::fillFrom(jv.toObject(), pod);
148 }
else if (!jv.isUndefined())
149 pod = fromJson<T>(jv);
153 QUOTIENT_API void reportEnumOutOfBounds(uint32_t v,
const char* enumTypeName);
164 template <
typename EnumT,
typename EnumStringValuesT>
165 inline std::optional<EnumT> enumFromJsonString(
const QString& s,
const EnumStringValuesT& enumValues)
167 static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
168 if (
const auto it = std::ranges::find(enumValues, s); it != cend(enumValues))
169 return static_cast<EnumT>(it - cbegin(enumValues));
183 template <
typename EnumT,
typename EnumStringValuesT>
184 inline QString enumToJsonString(EnumT v,
const EnumStringValuesT& enumValues)
186 static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
187 if (v < size(enumValues))
188 return enumValues[v];
190 _impl::reportEnumOutOfBounds(
static_cast<uint32_t>(v),
191 qt_getEnumName(EnumT()));
205 template <
typename FlagT,
typename FlagStringValuesT>
206 inline std::optional<FlagT> flagFromJsonString(
const QString& s,
const FlagStringValuesT& flagValues)
209 static_assert(std::is_unsigned_v<std::underlying_type_t<FlagT>>);
210 if (
const auto it = std::ranges::find(flagValues, s); it != cend(flagValues))
211 return static_cast<FlagT>(1U << (it - cbegin(flagValues)));
216 template <
typename FlagT,
typename FlagStringValuesT>
217 inline QString flagToJsonString(FlagT v,
const FlagStringValuesT& flagValues)
219 static_assert(std::is_unsigned_v<std::underlying_type_t<FlagT>>);
220 if (
const auto offset = std::countr_zero(std::to_underlying(v)); offset < ssize(flagValues))
221 return flagValues[offset];
223 _impl::reportEnumOutOfBounds(
static_cast<uint32_t>(v), qt_getEnumName(FlagT()));
231 inline bool fromJson(
const QJsonValue& jv) {
return jv.toBool(); }
234 inline int fromJson(
const QJsonValue& jv) {
return jv.toInt(); }
237 inline double fromJson(
const QJsonValue& jv) {
return jv.toDouble(); }
240 inline float fromJson(
const QJsonValue& jv) {
return float(jv.toDouble()); }
243 inline qint64 fromJson(
const QJsonValue& jv) {
return qint64(jv.toDouble()); }
246 inline QString fromJson(
const QJsonValue& jv) {
return jv.toString(); }
254 inline QByteArray fromJson(
const QJsonValue& jv) =
delete;
257 inline QJsonArray fromJson(
const QJsonValue& jv) {
return jv.toArray(); }
260 inline QJsonArray fromJson(
const QJsonDocument& jd) {
return jd.array(); }
262 inline QJsonValue toJson(
const QDateTime& val)
264 return val.isValid() ? val.toMSecsSinceEpoch() : QJsonValue();
267 inline QDateTime fromJson(
const QJsonValue& jv)
269 return QDateTime::fromMSecsSinceEpoch(fromJson<qint64>(jv),
270 #if QT_VERSION >= QT_VERSION_CHECK(6
, 5
, 0
)
277 inline QJsonValue toJson(
const QDate& val) {
return toJson(val.startOfDay()); }
279 inline QDate fromJson(
const QJsonValue& jv)
281 return fromJson<QDateTime>(jv).date();
289 struct JsonConverter<QUrl> {
290 static auto load(
const QJsonValue& jv)
292 return QUrl(jv.toString());
294 static auto dump(
const QUrl& url)
296 return url.toString(QUrl::FullyEncoded);
302 static QJsonValue dump(
const QVariant& v);
303 static QVariant load(
const QJsonValue& jv);
306 template <
typename... Ts>
307 inline QJsonValue toJson(
const std::variant<Ts...>& v)
313 [](
const auto& value) {
return QJsonValue { toJson(value) }; }, v);
316 template <
typename T>
317 struct JsonConverter<std::variant<QString, T>> {
318 static std::variant<QString, T> load(
const QJsonValue& jv)
321 return fromJson<QString>(jv);
322 return fromJson<T>(jv);
326 template <
typename T>
327 struct JsonConverter<std::optional<T>> {
328 static QJsonValue dump(
const std::optional<T>& from)
330 return from.has_value() ? toJson(*from) : QJsonValue();
332 static std::optional<T> load(
const QJsonValue& jv)
334 if (jv.isUndefined() || jv.isNull())
336 return fromJson<T>(jv);
340 template <
typename ContT>
341 struct JsonArrayConverter {
342 static auto dump(
const ContT& vals)
345 for (
const auto& v : vals)
346 ja.push_back(toJson(v));
349 static auto load(
const QJsonArray& ja)
352 vals.reserve(
static_cast<
typename ContT::size_type>(ja.size()));
357 for (
const auto& v : ja)
358 vals.push_back(fromJson<
typename ContT::value_type, QJsonValue>(v));
361 static auto load(
const QJsonValue& jv) {
return load(jv.toArray()); }
362 static auto load(
const QJsonDocument& jd) {
return load(jd.array()); }
365 template <
typename T>
366 struct JsonConverter<std::vector<T>>
367 :
public JsonArrayConverter<std::vector<T>> {};
369 template <
typename T, size_t N>
370 struct JsonConverter<std::array<T, N>> {
378 static constexpr std::make_index_sequence<N> Indices{};
379 template <
typename TargetT, size_t... I>
380 static auto staticTransform(
const auto& source, std::index_sequence<I...>,
383 return TargetT { unaryFn(source[I])... };
385 static auto dump(
const std::array<T, N> a)
387 return staticTransform<QJsonArray>(a, Indices, [](
const T& v) {
391 static auto load(
const QJsonArray& ja)
393 return staticTransform<std::array<T, N>>(ja, Indices,
394 fromJson<T, QJsonValue>);
398 template <
typename T>
399 struct JsonConverter<QList<T>> :
public JsonArrayConverter<QList<T>> {};
402 struct JsonConverter<QStringList> :
public JsonArrayConverter<QStringList> {
403 static auto dump(
const QStringList& sl)
405 return QJsonArray::fromStringList(sl);
410 struct JsonObjectConverter<QSet<QString>> {
411 static void dumpTo(QJsonObject& json,
const QSet<QString>& s)
413 for (
const auto& e : s)
414 json.insert(e, QJsonObject {});
416 static void fillFrom(
const QJsonObject& json, QSet<QString>& s)
418 s.reserve(s.size() + json.size());
419 for (
auto it = json.begin(); it != json.end(); ++it)
424 template <
typename HashMapT>
425 struct HashMapFromJson {
426 static void dumpTo(QJsonObject& json,
const HashMapT& hashMap)
428 for (
auto it = hashMap.begin(); it != hashMap.end(); ++it)
429 json.insert(it.key(), toJson(it.value()));
431 static void fillFrom(
const QJsonObject& jo, HashMapT& h)
433 h.reserve(h.size() + jo.size());
436 for (
auto it = jo.begin(); it != jo.end(); ++it)
437 h[it.key()] = fromJson<
typename HashMapT::mapped_type, QJsonValue>(
442 template <
typename T,
typename HashT>
443 struct JsonObjectConverter<std::unordered_map<QString, T, HashT>>
444 :
public HashMapFromJson<std::unordered_map<QString, T, HashT>> {};
446 template <
typename T>
447 struct JsonObjectConverter<QHash<QString, T>>
448 :
public HashMapFromJson<QHash<QString, T>> {};
452 QVariantHash
QUOTIENT_API fromJson(
const QJsonValue& jv);
456 constexpr bool IfNotEmpty =
false;
459 template <
typename KeyT,
typename ValT>
460 inline void addTo(QJsonObject& o, KeyT&& k, ValT&& v)
462 o.insert(std::forward<KeyT>(k), toJson(std::forward<ValT>(v)));
465 inline void addTo(QUrlQuery& q,
const QString& k,
auto v)
466 requires requires { u"%1"_s.arg(v); }
468 q.addQueryItem(k, u"%1"_s.arg(v));
473 inline void addTo(QUrlQuery& q,
const QString& k,
bool v)
475 q.addQueryItem(k, v ? u"true"_s : u"false"_s);
478 inline void addTo(QUrlQuery& q,
const QString& k,
const QUrl& v)
480 q.addQueryItem(k, QString::fromLatin1(v.toEncoded()));
483 inline void addTo(QUrlQuery& q,
const QString& k,
const QStringList& vals)
485 for (
const auto& v : vals)
486 q.addQueryItem(k, v);
489 template <
typename ValT>
490 inline void addTo(QUrlQuery& q,
const QString&,
const QHash<QString, ValT>& fields)
492 for (
const auto& [k, v] : fields.asKeyValueRange())
498 template <
typename ValT,
bool Force =
true>
500 template <
typename KeyT,
typename ForwardedT>
501 static void impl(
auto& container, KeyT&& key, ForwardedT&& value)
503 addTo(container, std::forward<KeyT>(key), std::forward<ForwardedT>(value));
508 template <
typename ValT>
509 requires requires(ValT v) { v.isEmpty(); }
510 struct AddNode<ValT, IfNotEmpty> {
511 template <
typename KeyT,
typename ForwardedT>
512 static void impl(
auto& container, KeyT&& key, ForwardedT&& value)
514 if (!value.isEmpty())
515 addTo(container, std::forward<KeyT>(key), std::forward<ForwardedT>(value));
520 template <
typename ValT>
521 struct AddNode<std::optional<ValT>, IfNotEmpty> {
522 template <
typename KeyT>
523 static void impl(
auto& container, KeyT&& key,
const auto& optValue)
526 addTo(container, std::forward<KeyT>(key), *optValue);
554 template <
bool Force =
true,
typename ContT,
typename KeyT,
typename ValT>
555 inline void addParam(ContT& container, KeyT&& key, ValT&& value)
557 _impl::AddNode<std::decay_t<ValT>, Force>::impl(container, std::forward<KeyT>(key),
558 std::forward<ValT>(value));
564 inline auto toSnakeCase(QLatin1String s)
566 QString result { s };
567 for (
auto it = result.begin(); it != result.end(); ++it)
569 const auto offset =
static_cast<
int>(it - result.begin());
570 result.insert(offset, u'_');
571 it = result.begin() + offset + 1;